/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <stdio.h>

#include "rdx_ilcomp.hpp"
#include "rdx_objectmanagement.hpp"
#include "rdx_opcodes.hpp"
#include "rdx_constants.hpp"
#include "rdx_intrinsics.hpp"

#define FLUSH_RESUME_FLAGS	\
	do {\
		if(resumeFlagCount != 0)\
		{\
			RDX_PROTECT(ctx, instructionResumeFlags->Push(ctx, resumeFlags));\
			resumeFlags = 0;\
			resumeFlagCount = 0;\
		}\
	} while(0)

#define PUSH_RESUME_FLAG(ili)	\
	do {\
		if(makeResumable || OpcodeIsResumable(ili.opcode))\
			resumeFlags |= (1 << resumeFlagCount);\
		makeResumable = false;\
		resumeFlagCount++;\
		if(resumeFlagCount == 8)\
			FLUSH_RESUME_FLAGS;\
	} while (0)

#define PUSH_INSTR_CA_1(ili, a0) do {\
		ili.arg.ca = compactArgs->list + compactArgs->count;\
		RDX_PROTECT(ctx, instructions->Push(ctx, ili) );\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a0));\
		instrNumIL++;\
		PUSH_RESUME_FLAG(ili);\
	} while(false)

#define PUSH_INSTR_CA_2(ili, a0, a1) do {\
		PUSH_INSTR_CA_1(ili, a0);\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a1));\
	} while(false)

#define PUSH_INSTR_CA_3(ili, a0, a1, a2) do {\
		PUSH_INSTR_CA_2(ili, a0, a1);\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a2));\
	} while(false)

#define PUSH_INSTR_CA_4(ili, a0, a1, a2, a3) do {\
		PUSH_INSTR_CA_3(ili, a0, a1, a2);\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a3));\
	} while(false)

#define PUSH_INSTR_CA_5(ili, a0, a1, a2, a3, a4) do {\
		PUSH_INSTR_CA_4(ili, a0, a1, a2, a3);\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a4));\
	} while(false)

#define PUSH_INSTR_CA_6(ili, a0, a1, a2, a3, a4, a5) do {\
		PUSH_INSTR_CA_5(ili, a0, a1, a2, a3, a4);\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a5));\
	} while(false)

#define PUSH_INSTR_CA_7(ili, a0, a1, a2, a3, a4, a5, a6) do {\
		PUSH_INSTR_CA_6(ili, a0, a1, a2, a3, a4, a5);\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a6));\
	} while(false)

#define PUSH_INSTR_CA_8(ili, a0, a1, a2, a3, a4, a5, a6, a7) do {\
		PUSH_INSTR_CA_7(ili, a0, a1, a2, a3, a4, a5, a6);\
		RDX_PROTECT(ctx, compactArgs->Push(ctx, a7));\
	} while(false)


#define PUSH_INSTR_LA_1(ili, a0) do {\
		ili.arg.la = largeArgs->list + largeArgs->count;\
		RDX_PROTECT(ctx, instructions->Push(ctx, ili) );\
		RDX_PROTECT(ctx, largeArgs->Push(ctx, a0));\
		instrNumIL++;\
		PUSH_RESUME_FLAG(ili);\
	} while(false)

#define PUSH_INSTR_LA_2(ili, a0, a1) do {\
		PUSH_INSTR_LA_1(ili, a0);\
		RDX_PROTECT(ctx, largeArgs->Push(ctx, a1));\
	} while(false)

#define PUSH_INSTR(ili)	do \
	{\
		ili.arg.la = NULL;\
		RDX_PROTECT(ctx, instructions->Push(ctx, ili) );\
		instrNumIL++;\
		PUSH_RESUME_FLAG(ili);\
	} while(false)

#define DECODE_INTEGER(T, dest) do	\
	{\
		T temp;\
		temp = static_cast<T>(decOp->hi1);\
		if(static_cast<HugeInt>(temp) != decOp->hi1)\
			RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);\
		dest = temp;\
	} while(false)

#define DECODE_FLOAT(T, dest) do	\
	{\
		if(sizeof(T) == sizeof(Float32))\
		{\
			union { Int32 i; T f; } RDX_MAY_ALIAS u;\
			DECODE_INTEGER(Int32, u.i);\
			dest = static_cast<T>(u.f);\
		}\
		else if(sizeof(T) == sizeof(Float64))\
		{\
			union { Int64 i; T f; } RDX_MAY_ALIAS u;\
			DECODE_INTEGER(Int64, u.i);\
			dest = static_cast<T>(u.f);\
		}\
		else\
		{\
			RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);\
		}\
	} while(false)

namespace RDX
{
	namespace ILCompiler
	{
		using namespace RDX::Programmability;
		using namespace RDX::ObjectManagement;
		using namespace RDX::Utility;
		using namespace RDX::ILOpcodes;

		// Opcodes that execution can potentially stop on, used to determine safe resumes.  This should include any opcode that can stop execution or throw an exception
		static const ILOpcode RESUMABLE_OPCODES[] =
		{
			ILOP_objinterior,
			ILOP_arrayindex,

			ILOP_call,
			ILOP_calldelegatebp,
			ILOP_calldelegateprv,
			ILOP_callvirtual,
			ILOP_callinterface,

			ILOP_verifynotnull,
			ILOP_newinstance,

			ILOP_jump,
			ILOP_jtrue,
			ILOP_jfalse,
			ILOP_jinherits,

			ILOP_tick,
			ILOP_assertenum,
			ILOP_assertinherits,

			ILOP_xnullref,
			ILOP_catch,
			ILOP_fatal,
			ILOP_throw,

			ILOP_iteratearray,
			ILOP_iteratearraysub,
		};

		template<typename _T>
		struct PushList
		{
			CRef<_T> list;
			LargeInt count;
			LargeInt maximum;

			inline PushList()
			{
				count = 0;
				maximum = 0;
				list = NULL;
			}

			inline void Push(OperationContext *ctx, const _T &ref)
			{
				if(list != NULL)
					list[count] = ref;
				if(!CheckAddOverflow(count, 1))
					RDX_LTHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);

				count++;
				if(count > maximum)
					maximum = count;
			}

			inline void ReinsertBottom(OperationContext *ctx, LargeInt offset, const _T &v)
			{
				if(list != NULL)
					list[offset] = v;
			}

			inline void ReinsertTop(OperationContext *ctx, LargeInt offset, const _T &v)
			{
				if(list != NULL)
					list[count - 1 - offset] = v;
			}

			inline _T RetrieveBottom(OperationContext *ctx, LargeInt offset)
			{
				if(offset < 0)
					RDX_LTHROWV(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND, _T());
				if(offset >= count)
					RDX_LTHROWV(ctx, Errors::RDX_ERROR_IL_STACK_UNDERFLOW, _T());
				if(list != NULL)
					return list[offset];
				_T instance;
				memset(&instance, 0, sizeof(instance));
				return instance;
			}

			inline _T RetrieveTop(OperationContext *ctx, LargeInt offset)
			{
				if(offset < 0)
					RDX_LTHROWV(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND, _T());
				if(count <= offset)
					RDX_LTHROWV(ctx, Errors::RDX_ERROR_IL_STACK_UNDERFLOW, _T());
				if(list != NULL)
					return list[count - 1 - offset];
				_T instance;
				memset(&instance, 0, sizeof(instance));
				return instance;
			}

			inline _T Pop(OperationContext *ctx)
			{
				if(!count)
					RDX_LTHROWV(ctx, Errors::RDX_ERROR_IL_STACK_UNDERFLOW, _T());
				count--;
				if(list != NULL)
					return list[count];
				_T instance;
				memset(&instance, 0, sizeof(instance));
				return instance;
			}
		};

		enum ILCompilePass
		{
			PASS_CreateStacks,
			PASS_GenerateCode,
			PASS_StitchJumps,

			PASS_Count,
		};

		enum VSTType
		{
			VST_Invalid,

			VST_Value,
			VST_Pointer,
			VST_ConstPointer,
			VST_Varying,
			VST_LocalRef,
			VST_Barrier,
			VST_ValueShell,

			VST_Indeterminate,	// Used for opstack checks that adjust by type
		};

		struct VStackValue
		{
			VSTType vstType;
			const Type *vType;
			LargeInt barrierValueCount;
			LargeInt size;
			LargeInt offset;
			LargeInt index;
			bool isFromLocal;				// Indicates that a pointer is definitely mutable (i.e. derived from a local)
			bool isNotNull;					// Indicates that the VStack can't contain a non-null
			bool demoted;
			LargeInt notNullInstruction;
			LargeInt creatingInstruction;	// For tracing barriers
			LargeInt sequentialID;
			LargeInt pointerSource;			// For frame-local pointers, the location of the pointer that this was based on
			Programmability::PointerSourceType pointerSourceType;

			bool isLoadedFromLocal;			// Indicates that a value was loaded from a local
			LargeInt loadedLocal;			// For local-loaded values, the index of the local that this was loaded from

			inline VStackValue()
			{
				vstType = VST_Invalid;
				vType = NULL;
				barrierValueCount = 0;
				offset = 0;
				index = 0;
				isFromLocal = false;
				isNotNull = false;
				isLoadedFromLocal = false;
				loadedLocal = 0;
				notNullInstruction = 0;
				pointerSource = 0;
				pointerSourceType = Programmability::PST_Object;
				demoted = false;
			}
		};

		struct VStackJournal
		{
			const Type *vType;
			LargeInt offset;
			LargeInt startInstruction;	// Mid-level IL instruction
			VSTType vstType;
			LargeInt sequentialID;
			LargeInt pointerSource;
			Programmability::PointerSourceType pointerSourceType;

			bool record;
		};
		
		struct VLocal
		{
			const Type *vType;
			bool isPointer;
			bool isParameter;
			bool isConstant;
			bool isNotNull;

			// CAUTION: The local offset calculation code will pull the top local, so parameterOffset and offset need to be different to avoid putting locals in an unusual spot
			LargeInt offset;
			LargeInt parameterOffset;
			LargeInt creatingInstruction;	// Used to determine valid jump targets
			const String *name;

			inline VLocal()
			{
				isPointer = false;
				isParameter = false;
				isNotNull = false;
				isConstant = false;
				vType = NULL;
				offset = 0;
				parameterOffset = 0;
			}
		};

		// Metadata for uncompiled instructions
		// Most tracking is done in translation space, relative to the original instruction set rather than the emitted IL instructions
		// Instructions are assumed to do 3 things in order: Intermediates, verifiers, and a single state modifying instruction.
		struct InstructionMeta
		{
			bool validJumpTarget;
			LargeInt translatedInstrNum;	// First IL instruction that executes the operation requested by the opcode, which may include validation.  Intermediates like ILOP_debuginfo and ILOP_tick are skipped.
			LargeInt numValidationInstrs;	// Number of instructions emitted preceding the state change causing instruction.  These are NOT skippable and may include validators.
			LargeInt minJumpRange;			// First instruction that can jump to this instruction
			LargeInt maxJumpRange;			// Last instruction that can jump to this instruction
		};

		struct ILDecodedOp
		{
			Opcode opcode;
			Int int1;
			Int int2;
			HugeInt hi1;
			Int intVarCount;
			const UInt8 *intVarStart;
			LargeInt intVarBytes;
			void *res;
			const String *str;
		};

		struct ILPassCompiler
		{
			Method *method;
			IObjectManager *objm;
			PushList<VStackValue> *opstack;
			PushList<VLocal> *localstack;
			PushList<ILInstruction> *instructions;
			PushList<UInt8> *instructionResumeFlags;
			PushList<ILOpCompactArg> *compactArgs;
			PushList<ILOpLargeArg> *largeArgs;
			PushList<VStackJournal> *stackjournal;
			PushList<StackJournal> *journal;
			PushList<ExceptionHandlerJournal> *exHandlers;
			ILDecodedOp *decOps;
			InstructionMeta *metalist;
			CRef<StructuredType> st_Exception;
			CRef<StructuredType> st_HashCode;
			ILCompilePass pass;

			LargeInt localOffsetMaximum;
			LargeInt opstackOffsetMaximum;
			LargeInt instrNum;
			LargeInt instrNumIL;
			LargeInt sequentialID;

			static inline bool VSTIsPointer(int vst)
			{
				return vst == VST_Pointer || vst == VST_ConstPointer;
			}

			void AssertOpstackEmptyOrBarrier(OperationContext *ctx)
			{
				RDX_TRY(ctx)
				{
					if(opstack->count == 0)
						return;
					if(pass > PASS_CreateStacks)
					{
						VStackValue vst;
						RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, 0) );
						if(vst.vstType != VST_Barrier)
							RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);
					}
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROW(ctx);
				}
				RDX_ENDTRY
			}

			LargeInt OpstackUsed(OperationContext *ctx, VSTType vstType, const Type *vType)
			{
				RDX_TRY(ctx)
				{
					LargeInt sizeUsed = 0;

					switch(vstType)
					{
					case VST_Varying:
						sizeUsed = sizeof(TypedRuntimePointer);
						break;
					case VST_Pointer:
					case VST_ConstPointer:
						sizeUsed = sizeof(RuntimePointer<void>);
						break;
					case VST_Value:
					case VST_ValueShell:
						if(objm->TypeIsObjectReference(vType))
							sizeUsed = sizeof(void*);
						else
						{
							if(GCInfo::From(vType)->containerType != objm->GetBuiltIns()->st_StructuredType)
								RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
								
							sizeUsed = static_cast<const StructuredType*>(vType)->_native.size;
							if(static_cast<const StructuredType*>(vType)->_native.alignment > sizeof(RuntimeStackValue))
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_UNALIGNABLE_BYVAL);
						}
						break;
					case VST_LocalRef:
					case VST_Barrier:
						sizeUsed = 0;
						break;
					};

					if(!CheckAddOverflow(sizeUsed, sizeof(RuntimeStackValue) - 1 ))
						RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);

					LargeInt basePadded = sizeUsed + static_cast<LargeInt>(sizeof(RuntimeStackValue)) - 1;
					sizeUsed = basePadded - (basePadded % sizeof(RuntimeStackValue));
					return sizeUsed;
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROWV(ctx, 0);
				}
				RDX_ENDTRY
			}

			void AppendOpstack(OperationContext *ctx, VStackValue &vst)
			{
				RDX_TRY(ctx)
				{
					if(pass > PASS_CreateStacks)
					{
						const GCInfo *gci = GCInfo::From(vst.vType);

						LargeInt sizeUsed;
						RDX_PROTECT_ASSIGN(ctx, sizeUsed, OpstackUsed(ctx, vst.vstType, vst.vType) );

						LargeInt opstackOffset = 0;
						if(pass > PASS_CreateStacks)
							opstackOffset = localOffsetMaximum;
						if(opstack->count)
							opstackOffset = -opstack->RetrieveTop(ctx, 0).offset;

						opstackOffset += sizeUsed;
						if(opstackOffset > opstackOffsetMaximum)
							opstackOffsetMaximum = opstackOffset;

						vst.size = sizeUsed;
						vst.offset = -opstackOffset;
						vst.sequentialID = sequentialID++;

						//if(vst.vType == NULL)
						//	RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_BAD_TYPE);
					}
					RDX_PROTECT(ctx, opstack->Push(ctx, vst) );
					
					VStackJournal sj;
					sj.startInstruction = instrNum;
					sj.record = false;
					sj.vType = vst.vType;
					sj.offset = vst.offset;
					sj.vstType = vst.vstType;
					sj.sequentialID = vst.sequentialID;
					sj.pointerSource = vst.pointerSource;
					sj.pointerSourceType = vst.pointerSourceType;
					RDX_PROTECT(ctx, stackjournal->Push(ctx, sj) );
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROW(ctx);
				}
				RDX_ENDTRY
			}

			void LivenShell(OperationContext *ctx, VStackValue &vst)
			{
				if(vst.vstType == VST_ValueShell)
				{
					vst.vstType = VST_Value;

					// Update the journal, this value isn't valid until the next instruction
					if(stackjournal->list)
					{
						for(LargeInt i=0;i<stackjournal->count;i++)
						{
							VStackJournal vsj = stackjournal->RetrieveBottom(ctx, i);
							if(vsj.sequentialID == vst.sequentialID)
							{
								vsj.startInstruction = instrNum;
								stackjournal->ReinsertBottom(ctx, i, vsj);
							}
						}
					}
				}
			}

			// Emits a journal entry that records a call-converted VStack value, then sets the to-be-emitted journal entry to use
			// the current instruction instead.  This is used to prevent journals from lower frames from being invalidated when
			// a higher frame overlaps it and potentially overwrites values with weaker-typed ones.  The journal can't simply be
			// changed type because a thread could potentially be restored to an earlier point in time while the journal exists, but
			// is used by a non-destructive operation like OP_clone
			void CallConvertOpstack(OperationContext *ctx, LargeInt topIndex, const Type *newType)
			{
				if(pass <= PASS_CreateStacks)
					return;

				RDX_TRY(ctx)
				{
					VStackValue vst;
					RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, topIndex) );
					
					VStackJournal vj;
					RDX_PROTECT_ASSIGN(ctx, vj, stackjournal->RetrieveTop(ctx, topIndex) );

					StackJournal sj;
					sj.startInstruction = vj.startInstruction;
					sj.endInstruction = instrNum - 1;	// Value stops existing prior to the current instruction

					if(sj.startInstruction != sj.endInstruction)	// Don't bother journaling this as a stricter type if it won't exist
					{
						sj.offset = vj.offset;
						sj.vType = vj.vType;
						sj.isPointer = VSTIsPointer(vj.vstType);
						sj.isConstant = (vj.vstType == VST_ConstPointer);
						sj.isVarying = (vj.vstType == VST_Varying);
						sj.isParameter = false;
						sj.isFromLocal = true;
						sj.isNotNull = vst.isNotNull;
						sj.notNullInstruction = vst.notNullInstruction;
						sj.name = NULL;
						sj.pointerSource = vj.pointerSource;
						sj.pointerSourceType = vj.pointerSourceType;
						if(vst.demoted)
							sj.vType = vst.vType;

						RDX_PROTECT(ctx, journal->Push(ctx, sj) );
					}

					vj.startInstruction = instrNum - 1;	// New value starts existing after the previous instruction
					RDX_PROTECT(ctx, stackjournal->ReinsertTop(ctx, topIndex, vj));

					// Demote the VStack value
					vst.demoted = true;
					vst.vType = newType;
					RDX_PROTECT(ctx, opstack->ReinsertTop(ctx, topIndex, vst) );
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROW(ctx);
				}
				RDX_ENDTRY
			}

			VStackValue PopOpstack(OperationContext *ctx, VSTType expectedType = ILCompiler::VST_Value)
			{
				RDX_TRY(ctx)
				{
					VStackValue vst;
					RDX_PROTECT_ASSIGN(ctx, vst, opstack->Pop(ctx) );
					
					VStackJournal vj;
					RDX_PROTECT_ASSIGN(ctx, vj, stackjournal->Pop(ctx) );
					if(pass > PASS_CreateStacks)
					{
						if(expectedType != VST_Indeterminate && !(expectedType == VST_ConstPointer && vst.vstType == VST_Pointer) && vst.vstType != expectedType)
							RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

						if(vj.record && vj.vstType != VST_Barrier && vj.vstType != VST_LocalRef)
						{
							StackJournal sj;
							sj.startInstruction = vj.startInstruction;
							sj.endInstruction = instrNum;
							sj.offset = vj.offset;
							sj.vType = vj.vType;
							sj.isPointer = VSTIsPointer(vj.vstType);
							sj.isConstant = (vj.vstType == VST_ConstPointer);
							sj.isVarying = (vj.vstType == VST_Varying);
							sj.isParameter = false;
							sj.isFromLocal = true;
							sj.isNotNull = vst.isNotNull;
							sj.notNullInstruction = vst.notNullInstruction;
							sj.name = NULL;
							sj.pointerSource = vj.pointerSource;
							sj.pointerSourceType = vj.pointerSourceType;

							if(vst.demoted)
								sj.vType = vst.vType;

							RDX_PROTECT(ctx, journal->Push(ctx, sj) );
						}
					}
					return vst;
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROWV(ctx, VStackValue());
				}
				RDX_ENDTRY
			}
			
			void AppendLocal(OperationContext *ctx, Type *t, LargeInt instruction, const String *name, bool isPointer = false, bool isParameter = false, bool isConstant = false, bool isNotNull = false)
			{
				RDX_TRY(ctx)
				{
					VLocal vl;

					if(t == NULL)
						RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

					vl.vType = t;
					vl.isPointer = isPointer;
					vl.isParameter = isParameter;
					vl.creatingInstruction = instruction;
					vl.name = name;
					vl.isNotNull = isNotNull;
					vl.isConstant = isConstant;

					LargeInt align = 1;
					LargeInt size = 0;
					if(vl.isPointer)
					{
						size = sizeof(RDX::Programmability::RuntimePointer<void>);
						align = ALIGN_RuntimePointer;
					}
					else
					{
						RDX_PROTECT(ctx, objm->TypeValueSize(ctx, t, size, align) );
					}

					if(isParameter)
					{
						LargeInt poffs = 0;
						bool overflowed = false;
							
						if(localstack->count)
							poffs = -localstack->RetrieveTop(ctx, 0).parameterOffset;
						LargeInt paddedSize = PaddedSize(size, sizeof(RuntimeStackValue), overflowed);
						if(overflowed)
							RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);

						if(!CheckAddOverflow(poffs, paddedSize))
							RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);
						poffs += paddedSize;

						vl.parameterOffset = -poffs;
					}
					else
					{
						LargeInt localOffset = 0;

						if(localstack->count)
							localOffset = -localstack->RetrieveTop(ctx, 0).offset;

						if(!CheckAddOverflow(localOffset, size))
							RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);
						localOffset += size;
						if(!CheckAddOverflow(localOffset, (align - 1)) ||
							!CheckAddOverflow(localOffset, (localOffset + (align - 1)) ) )
							RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);

						localOffset = (localOffset + (align - 1));
						localOffset -= localOffset % align;

						if(localOffset > localOffsetMaximum)
							localOffsetMaximum = localOffset;
						vl.offset = -localOffset;

						RDX_VERBOSE( wprintf(L"Local %i: %s\n", vl.offset, GCInfo::From(vl.vType)->gstSymbol->_native.characters) );
					}
					

					VStackJournal sj;
					sj.startInstruction = instrNum;
					sj.record = false;
					sj.vType = t;
					sj.offset = vl.offset;
					sj.vstType = VST_Value;
					sj.sequentialID = sequentialID++;

					RDX_PROTECT(ctx, stackjournal->Push(ctx, sj) );

					RDX_PROTECT(ctx, localstack->Push(ctx, vl) );
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROW(ctx);
				}
				RDX_ENDTRY
			}

			VLocal PopLocal(OperationContext *ctx)
			{
				RDX_TRY(ctx)
				{
					VLocal vl;
					RDX_PROTECT_ASSIGN(ctx, vl, localstack->Pop(ctx) );
					
					VStackJournal vj;
					RDX_PROTECT_ASSIGN(ctx, vj, stackjournal->Pop(ctx) );

					if(pass > PASS_CreateStacks)
					{
						if(vj.record)
						{
							StackJournal sj;
							sj.startInstruction = vj.startInstruction;
							sj.endInstruction = instrNum;
							sj.offset = vj.offset;
							sj.vType = vj.vType;
							sj.isPointer = false;
							sj.isVarying = false;
							sj.isParameter = false;
							sj.isFromLocal = false;
							sj.isNotNull = false;
							sj.notNullInstruction = 0;
							sj.isConstant = false;
							sj.name = vl.name;
							sj.pointerSource = 0;
							sj.pointerSourceType = Programmability::PST_Invalid;

							RDX_PROTECT(ctx, journal->Push(ctx, sj) );
						}
					}

					return vl;
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROWV(ctx, VLocal());
				}
				RDX_ENDTRY
			}

			// Synchronizes the journal with the current opstack and localstack state.  Anything on the opstack at the time of call
			// can be traced.  This should be done on any instruction that can suspend the thread or throw an exception.
			void SyncJournal(OperationContext *ctx)
			{
				RDX_TRY(ctx)
				{
					if(stackjournal->list)
					{
						for(LargeInt i=0;i<stackjournal->count;i++)
						{
							VStackJournal vj = stackjournal->RetrieveBottom(ctx, i);
							vj.record = true;
							stackjournal->ReinsertBottom(ctx, i, vj);
						}
					}
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROW(ctx);
				}
				RDX_ENDTRY
			}

			static bool TypeIsRefStruct(IObjectManager *objm, const Type *t)
			{
				if(t == NULL || GCInfo::From(t)->containerType != objm->GetBuiltIns()->st_StructuredType)
					return false;
				const StructuredType *st = static_cast<const StructuredType*>(t);
				return st->storageSpecifier == EnumValue::From(StructuredType::SS_RefStruct);
			}

			LargeInt FindJumpTarget(LargeInt ilinstr)
			{
				if(pass < PASS_StitchJumps)
					return ilinstr;

				const ILInstruction *instr = instructions->list + ilinstr;
				// Skip debuginfo instructions
				while(instr->opcode == ILOP_debuginfo)
				{
					ilinstr++;
					instr++;
				}
				return ilinstr;
			}

			LargeInt StitchJump(OperationContext *ctx, LargeInt startInstr, LargeInt destInstr)
			{
				if(pass < PASS_StitchJumps)
					return 0;

				RDX_TRY(ctx)
				{
					if(destInstr < 0 || destInstr >= GCInfo::From(metalist)->numElements)
						RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_JUMP_TARGET);
					InstructionMeta *meta = metalist + destInstr;
					if(startInstr < meta->minJumpRange || startInstr > meta->maxJumpRange || !meta->validJumpTarget)
						RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_JUMP_TARGET);

					return FindJumpTarget(meta->translatedInstrNum);
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROWV(ctx, 0);
				}
				RDX_ENDTRY
			}

			static bool OpcodeIsResumable(ILOpcode opcode)
			{
				for(LargeInt i=0;i<sizeof(RESUMABLE_OPCODES)/sizeof(RESUMABLE_OPCODES[0]);i++)
					if(RESUMABLE_OPCODES[i] == opcode)
						return true;
				return false;
			}

			template<class _Ti>
			inline static bool DecodeInt(const Byte *&bytecode, LargeInt &bytesRemaining, _Ti &out)
			{
				if(!bytesRemaining)
					return false;

				Byte b = *bytecode++;
				bytesRemaining--;

				_Ti v;
				v = (b & 0x7f);
				if(b & 0x40)
					v = (b & 0x3f) - 64;

				while(b & 0x80)
				{
					if(!bytesRemaining)
						return false;
					b = *bytecode++;
					bytesRemaining--;
					v = static_cast<_Ti>((v << 7) | (b & 0x7f));
				}
				out = v;
				return true;
			}

			void CompilePass(OperationContext *ctx)
			{
				RDX_TRY(ctx)
				{
					LargeInt numDecodedInstructions = method->numInstructions;
					LargeInt currentEHLastInstruction = -1;		// The last instruction covered by an exception handler.  Used to determine if tail call optimizations are possible.

					LargeInt numMethodParameters = 0;
					LargeInt numMethodReturnTypes = 0;
					UInt8 resumeFlags = 0;
					int resumeFlagCount = 0;
					bool makeResumable = false;
					LargeInt lastDebugInfoInstruction = -1;

					if(method->parameters)
						numMethodParameters = GCInfo::From(method->parameters)->numElements;
					if(method->returnTypes)
						numMethodReturnTypes = GCInfo::From(method->returnTypes)->numElements;

					for(LargeInt i=0;i<numMethodReturnTypes;i++)
					{
						Type *t = method->returnTypes[i];
						if(t == objm->GetBuiltIns()->st_Varying)
							RDX_STHROW(ctx, Errors::RDX_ERROR_FORBIDDEN_VARYING_USAGE);
					}

					// CAUTION: The local stack expects parameters to be first in numerous places, i.e. AppendLocal and tail call checks
					for(LargeInt i=0;i<numMethodParameters;i++)
					{
						MethodParameter *param = method->parameters + i;
						
						// This should be caught earlier, but be safe...
						if(param->type == NULL)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						bool isPointer = TypeIsRefStruct(objm, param->type);

						if(param->type == objm->GetBuiltIns()->st_Varying)
							RDX_STHROW(ctx, Errors::RDX_ERROR_FORBIDDEN_VARYING_USAGE);

						// NotNull parameters must be constant
						if(param->isNotNull != FalseValue && param->isConstant == FalseValue)
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

						RDX_PROTECT(ctx, AppendLocal(ctx, param->type, -1, NULL, isPointer, true, (param->isConstant != FalseValue), (param->isNotNull != FalseValue) ) );

						{
							StackJournal sj;
							sj.isParameter = true;
							sj.isFromLocal = false;
							sj.isVarying = false;
							sj.startInstruction = 0;
							sj.endInstruction = 0;
							sj.isPointer = isPointer;
							sj.offset = localstack->RetrieveTop(ctx, 0).parameterOffset;
							sj.pointerSource = sj.offset;
							sj.pointerSourceType = Programmability::PST_Parameter;
							sj.vType = param->type;
							sj.name = NULL;
							sj.isConstant = (param->isConstant != FalseValue);
							sj.isNotNull = (param->isNotNull != FalseValue);
							sj.notNullInstruction = 0;

							RDX_PROTECT(ctx, journal->Push(ctx, sj) );
							
							if(i == method->thisParameterOffset - 1)
								method->_native.thisParameterInvokeOffset = sj.offset;
						}
					}
					
					LargeInt numFileInfos = 0;
					LargeInt currentFileInfo = 0;
					const InstructionFileInfo *firstFileInfo = method->instructionFileInfos;
					if(firstFileInfo != NULL) numFileInfos = GCInfo::From(firstFileInfo)->numElements;

					instrNumIL = 0;
					for(instrNum=0;instrNum<numDecodedInstructions;instrNum++)
					{
						const ILDecodedOp *decOp = decOps + instrNum;

						InstructionMeta *meta = metalist + instrNum;

						meta->translatedInstrNum = instructions->count;
						meta->numValidationInstrs = 0;

						if(pass < PASS_StitchJumps)
						{
							meta->minJumpRange = 0;
							meta->maxJumpRange = numDecodedInstructions - 1;
						}

						meta->validJumpTarget = false;
						if(pass > PASS_CreateStacks)
							if(opstack->count == 0 || opstack->RetrieveTop(ctx, 0).vstType == VST_Barrier)
								meta->validJumpTarget = true;

						if(currentFileInfo != numFileInfos && firstFileInfo[currentFileInfo].firstInstruction == instrNum)
						{
							if(instructions->count == lastDebugInfoInstruction)
							{
								// Last instruction was a debuginfo, just remap it
								ILOpCompactArg opArg0, opArg1;
								RDX_PROTECT_ASSIGN(ctx, opArg1, compactArgs->Pop(ctx));
								RDX_PROTECT_ASSIGN(ctx, opArg0, compactArgs->Pop(ctx));
								RDX_PROTECT(ctx, compactArgs->Push(ctx, opArg0));
								RDX_PROTECT(ctx, compactArgs->Push(ctx, opArg1));
							}
							else
							{
								ILInstruction ili;
								bool prevResumable = makeResumable;
								ili.opcode = ILOP_debuginfo;

								ILOpCompactArg opArg0, opArg1;
								opArg0.p = firstFileInfo[currentFileInfo].filename;
								opArg1.li = firstFileInfo[currentFileInfo].line;

								meta->translatedInstrNum++;

								PUSH_INSTR_CA_2(ili, opArg0, opArg1);
								makeResumable = prevResumable;					// Carry through resumability
							}

							currentFileInfo++;
							lastDebugInfoInstruction = instructions->count;
						}

						switch(decOp->opcode)
						{
						case OP_startbarrier:
							{
								// Previous stack values must be shells or local pointers
								if(pass > PASS_CreateStacks)
								{
									if(decOp->int1 < 1)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									for(LargeInt i=0;i<decOp->int1;i++)
									{
										VStackValue vst;
										RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, i) );

										bool expectPointer = TypeIsRefStruct(objm, vst.vType);
										// Pointers need to be to locals, everything else must be a shell
										if( (expectPointer && (vst.vstType != VST_Pointer || !vst.isFromLocal)) &&
											(!expectPointer && vst.vstType != VST_ValueShell) )
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									}
								}

								VStackValue vst;
								vst.vstType = VST_Barrier;
								vst.vType = NULL;
								vst.barrierValueCount = decOp->int1;
								vst.creatingInstruction = instrNum;
								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );
							}
							break;
						case OP_endbarrier:
							{
								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx, VST_Barrier) );

								if(pass > PASS_CreateStacks)
								{
									if(instrNum == 0 || decOps[instrNum-1].opcode != OP_return)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

									for(LargeInt i=vst.creatingInstruction;i<instrNum;i++)
									{
										if(metalist[i].minJumpRange < vst.creatingInstruction)
											metalist[i].minJumpRange = vst.creatingInstruction;
										if(metalist[i].maxJumpRange > instrNum)
											metalist[i].maxJumpRange = instrNum;
									}

									VStackValue rvVst;
									for(LargeInt i=0;i<vst.barrierValueCount;i++)
									{
										RDX_PROTECT_ASSIGN(ctx, rvVst, opstack->RetrieveTop(ctx, i) );
										if(rvVst.vstType == VST_ValueShell)
										{
											RDX_PROTECT(ctx, LivenShell(ctx, rvVst) );
											RDX_PROTECT(ctx, opstack->ReinsertTop(ctx, i, rvVst) );
										}
									}
								}
							}

							{
								ILInstruction ili;
								ili.opcode = ILCompiler::ILOP_hardenstack;
								PUSH_INSTR(ili);
							}
							break;
						case OP_throw:
							{
								RDX_PROTECT(ctx, SyncJournal(ctx) );

								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx) );

								if(pass > PASS_CreateStacks)
								{
									if(!vst.vType || !objm->TypesCompatible(vst.vType, st_Exception))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									ILInstruction ili;
									ili.opcode = ILCompiler::ILOP_throw;

									ILOpCompactArg opArg0;
									opArg0.li = vst.offset;

									PUSH_INSTR_CA_1(ili, opArg0);
								}
							}
							break;
						case OP_try:
							{
								// Catch label must be a "catch" that catches Core.Exception
								// Opstack must be empty and must not be within a barrier

								if(opstack->count)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);
								
								LargeInt spanEndInstruction = instrNum + decOp->int2;
								LargeInt catchInstruction = instrNum + decOp->int1;

								if(spanEndInstruction < 0 || spanEndInstruction >= numDecodedInstructions)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								if(catchInstruction < 0 || catchInstruction >= numDecodedInstructions)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								if(pass > PASS_CreateStacks)
								{
									if(decOps[catchInstruction].opcode != OP_catch ||
										decOps[catchInstruction].res != this->st_Exception)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_JUMP_TARGET);
								}

								if(catchInstruction == numDecodedInstructions-1)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_STACKS_UNBALANCED);	// This should get caught by the balancer?

								ExceptionHandlerJournal ehj;
								ehj.startInstruction = instrNum + 1;
								ehj.endInstruction = spanEndInstruction - 1;
								ehj.handlerInstruction = catchInstruction + 1;	// The actual catch instruction is poisonous

								if(ehj.endInstruction > currentEHLastInstruction)
									currentEHLastInstruction = ehj.endInstruction;

								// The catch and end both need to be reachable from here even if not used
								RDX_PROTECT(ctx, StitchJump(ctx, instrNum, spanEndInstruction) );
								RDX_PROTECT(ctx, StitchJump(ctx, instrNum, catchInstruction) );

								RDX_PROTECT(ctx, exHandlers->Push(ctx, ehj) );
							}
							break;
						case OP_catch:
							{
								// Opstack must be empty and must not be within a barrier
								if(opstack->count)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

								VStackValue vst;
								vst.vstType = VST_Value;

								Type *t = static_cast<Type*>(decOp->res);
								if(!t ||
									!objm->TypesCompatible(GCInfo::From(t)->containerType, objm->GetBuiltIns()->st_Type) ||
									!objm->TypesCompatible(t, st_Exception) )
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								vst.vType = t;

								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

								ILInstruction ili;
								ili.opcode = ILCompiler::ILOP_catch;

								PUSH_INSTR(ili);
							}
							break;
						case OP_trycast:
							{
								// Instruction at target label must be a "catch" of the same type as the trycast
								// Opstack must contain only the value being cast and must not be within a barrier

								if(opstack->count != 1)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);
								
								RDX_PROTECT(ctx, SyncJournal(ctx) );

								LargeInt catchInstruction = instrNum + decOp->int1;

								if(catchInstruction < 0 || catchInstruction >= numDecodedInstructions)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								Type *ext = static_cast<Type*>(decOp->res);
								if(!ext ||
									!objm->TypesCompatible(GCInfo::From(ext)->containerType, objm->GetBuiltIns()->st_Type) ||
									!objm->TypesCompatible(ext, st_Exception) )
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								if(pass > PASS_CreateStacks)
								{
									if(decOps[catchInstruction].opcode != OP_catch ||
										decOps[catchInstruction].res != ext)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_JUMP_TARGET);
								}

								if(catchInstruction == numDecodedInstructions-1)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_STACKS_UNBALANCED);	// This should get caught by the balancer?

								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, 0) );

								if(pass > PASS_CreateStacks)
								{
									if(vst.vstType != VST_Value || vst.vType != st_Exception)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									ILInstruction ili;
									ILOpCompactArg opArg0;
									ILOpCompactArg opArg1;
									ILOpCompactArg opArg2;
									if(ext == st_Exception)		// Compiler's a bit stupid about this, optimize
									{
										ili.opcode = ILCompiler::ILOP_jump;
									}
									else
									{
										ili.opcode = ILCompiler::ILOP_jinherits;
										opArg2.p = ext;
									}

									// Catch needs to be reachable
									RDX_PROTECT(ctx, StitchJump(ctx, instrNum, catchInstruction) );

									RDX_PROTECT_ASSIGN(ctx, opArg0.li, FindJumpTarget(metalist[catchInstruction + 1].translatedInstrNum) );
									opArg1.li = vst.offset;

									if(ext == st_Exception)
										PUSH_INSTR_CA_1(ili, opArg0);	// ILOP_jump
									else
										PUSH_INSTR_CA_3(ili, opArg0, opArg1, opArg2);	// ILOP_jinherits
								}
							}
							break;
						case OP_jump:
							{
								if(pass > PASS_CreateStacks)
								{
									// Stack must be empty or with only a barrier remaining
									if(opstack->count != 0 && opstack->RetrieveTop(ctx, 0).vstType != VST_Barrier)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

									LargeInt targetInstruction = instrNum + decOp->int1;
									if(targetInstruction < 0 || targetInstruction >= numDecodedInstructions)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									RDX_PROTECT(ctx, SyncJournal(ctx) );

									if(decOp->int1 <= 0)
									{
										ILInstruction tickILI;
#ifdef RDX_ENABLE_TIMESLICE_COUNTER
										tickILI.opcode = ILOP_tick;
#else
										tickILI.opcode = ILOP_hardenstack;
#endif
										PUSH_INSTR(tickILI);
										meta->translatedInstrNum++;
									}

									ILInstruction ili;
									ILOpCompactArg opArg0;
									ili.opcode = ILCompiler::ILOP_jump;
									RDX_PROTECT_ASSIGN(ctx, opArg0.li, StitchJump(ctx, instrNum, targetInstruction) );

									PUSH_INSTR_CA_1(ili, opArg0);
								}
							}
							break;
						case OP_jumpif:
						case OP_jumpifnot:
							{
								// NOTE: Demotion is not implemented for this because currently, no jumping intrinsics modify parameters
								Method *cmethod = static_cast<Method*>(decOp->res);
								if(!cmethod || GCInfo::From(cmethod)->containerType != objm->GetBuiltIns()->st_Method)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								const GCInfo *mi = GCInfo::From(cmethod);

								cmethod->DetermineIntrinsicState();
								if(!cmethod->_native.isBranching)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								if(decOp->int1 <= 0)
								{
									ILInstruction tickILI;
#ifdef RDX_ENABLE_TIMESLICE_COUNTER
									tickILI.opcode = ILOP_tick;
#else
									tickILI.opcode = ILOP_hardenstack;
#endif
									PUSH_INSTR(tickILI);
									meta->translatedInstrNum++;
								}

								RDX_PROTECT(ctx, SyncJournal(ctx) );	// Sync now before parameters get removed

								const MethodParameter *params = cmethod->parameters;
								LargeInt numParams = 0;
								if(params)
									numParams = GCInfo::From(params)->numElements;
								
								LargeInt paramLocation = 0;
								if(opstack->count)
									paramLocation = opstack->RetrieveTop(ctx, 0).offset;

								// Method signature must match, values must be of the expected type (i.e. POINTER to STRUCT is not allowed)
								for(LargeInt i=0;i<numParams;i++)
								{
									LargeInt pi = numParams - 1 - i;
									VSTType expectedType = VST_Value;
									if(params[pi].type == NULL)
										RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

									if(params[pi].type == objm->GetBuiltIns()->st_Varying)
										expectedType = VST_Varying;
									else if(TypeIsRefStruct(objm, params[pi].type))
									{
										if(params[pi].isConstant)
											expectedType = VST_ConstPointer;
										else
											expectedType = VST_Pointer;
									}

									VStackValue paramVST;
									RDX_PROTECT_ASSIGN(ctx, paramVST, PopOpstack(ctx, expectedType) );

									if(pass > PASS_CreateStacks)
									{
										if(!objm->TypesCompatible(paramVST.vType, params[pi].type))
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									}
								}

								// Stack needs to be clear or a barrier after params
								if(pass > PASS_CreateStacks)
								{
									if(opstack->count != 0)
									{
										VStackValue stackTop = opstack->RetrieveTop(ctx, 0);
										if(stackTop.vstType != VST_Barrier)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);
									}
								}
								
								LargeInt targetInstruction = instrNum + decOp->int1;
								if(targetInstruction < 0 || targetInstruction >= numDecodedInstructions)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								ILInstruction ili;
								ILOpCompactArg opArg0, opArg1, opArg2;
								ili.opcode = ILCompiler::ILOP_jump;
								RDX_PROTECT_ASSIGN(ctx, opArg0.li, StitchJump(ctx, instrNum, targetInstruction) );
								opArg1.li = paramLocation;
								opArg2.li = cmethod->_native.p2;

								if(decOp->opcode == OP_jumpif)
									ili.opcode = cmethod->_native.opcode;
								else
									ili.opcode = cmethod->_native.falseCheckOpcode;

								PUSH_INSTR_CA_3(ili, opArg0, opArg1, opArg2);
							}
							break;
						case OP_jumpiftrue:
						case OP_jumpiffalse:
							{
								RDX_PROTECT(ctx, SyncJournal(ctx) );
								
								if(decOp->int1 <= 0)
								{
									ILInstruction tickILI;
#ifdef RDX_ENABLE_TIMESLICE_COUNTER
									tickILI.opcode = ILOP_tick;
#else
									tickILI.opcode = ILOP_hardenstack;
#endif
									PUSH_INSTR(tickILI);
									meta->translatedInstrNum++;
								}

								VStackValue bvVst;
								RDX_PROTECT_ASSIGN(ctx, bvVst, PopOpstack(ctx, VST_Value) );
	
								if(pass > PASS_CreateStacks)
								{
									if(opstack->count != 0 && opstack->RetrieveTop(ctx, 0).vstType != VST_Barrier)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

									if(bvVst.vType != objm->GetBuiltIns()->st_Bool)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									LargeInt targetInstruction = instrNum + decOp->int1;
									if(targetInstruction < 0 || targetInstruction >= numDecodedInstructions)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									ILInstruction ili;
									if(decOp->opcode == OP_jumpiftrue)
										ili.opcode = ILCompiler::ILOP_jtrue;
									else
										ili.opcode = ILCompiler::ILOP_jfalse;

									ILOpCompactArg opArg0, opArg1;
									RDX_PROTECT_ASSIGN(ctx, opArg0.li, StitchJump(ctx, instrNum, targetInstruction) );
									opArg1.li = bvVst.offset;

									PUSH_INSTR_CA_2(ili, opArg0, opArg1);
								}
							}
							break;
						case OP_jumpifequal:
						case OP_jumpifnotequal:
							{
								RDX_PROTECT(ctx, SyncJournal(ctx) );

								VStackValue vst1, vst2;
								RDX_PROTECT_ASSIGN(ctx, vst1, PopOpstack(ctx, VST_Indeterminate) );
								RDX_PROTECT_ASSIGN(ctx, vst2, PopOpstack(ctx, VST_Indeterminate) );
	
								if(pass > PASS_CreateStacks)
								{
									if(vst1.vstType != VST_Value && !VSTIsPointer(vst1.vstType))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									if(vst2.vstType != VST_Value && !VSTIsPointer(vst2.vstType))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(!objm->TypesCompatible(vst1.vType, vst2.vType) && !objm->TypesCompatible(vst2.vType, vst1.vType))
										RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

									bool expectRef = TypeIsRefStruct(objm, vst1.vType);
									if(expectRef && (!VSTIsPointer(vst1.vstType) || !VSTIsPointer(vst2.vstType)))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									if(!expectRef && (vst1.vstType != VST_Value || vst2.vstType != VST_Value))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									LargeInt targetInstruction = instrNum + decOp->int1;
									if(targetInstruction < 0 || targetInstruction >= numDecodedInstructions)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									LargeInt size, align;
									RDX_PROTECT(ctx, objm->TypeValueSize(ctx, vst1.vType, size, align) );

									ILInstruction ili;
									if(decOp->opcode == OP_jumpifequal)
									{
										if(expectRef)
											ili.opcode = ILCompiler::ILOP_jeq_p;
										else
											ili.opcode = ILCompiler::ILOP_jeq_f;
									}
									else
									{
										if(expectRef)
											ili.opcode = ILCompiler::ILOP_jne_p;
										else
											ili.opcode = ILCompiler::ILOP_jne_f;
									}
									ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
									RDX_PROTECT_ASSIGN(ctx, opArg0.li, StitchJump(ctx, instrNum, targetInstruction) );
									opArg1.li = vst1.offset;
									opArg2.li = vst2.offset;
									opArg3.li = size;
									opArg4.li = align;

									PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
								}
							}
							break;
						case OP_callvirtual:
						case OP_call:
						case OP_calldelegate:
							{
								MethodParameter *params = NULL;
								Type **returnTypes = NULL;
								Method *calledMethod = NULL;
								DelegateType *dt = NULL;
								const StructuredType *bdt = NULL;

								if(decOp->opcode == OP_call || decOp->opcode == OP_callvirtual)
								{
									calledMethod = static_cast<Method*>(decOp->res);

									GCInfo *cmi = GCInfo::From(calledMethod);
									if(!calledMethod || GCInfo::From(calledMethod)->containerType != objm->GetBuiltIns()->st_Method)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									calledMethod->DetermineIntrinsicState();
									if(!calledMethod->_native.neverFails)
										RDX_PROTECT(ctx, SyncJournal(ctx) );	// Sync now before parameters get removed
									params = calledMethod->parameters;
									returnTypes = calledMethod->returnTypes;

									// Handle timeouts if this is not an intrinsic
									if(decOp->opcode != OP_call || !calledMethod->_native.isIntrinsic)
									{
										ILInstruction tickILI;
										tickILI.opcode = ILOP_tick;
										PUSH_INSTR(tickILI);
										meta->translatedInstrNum++;
									}
								}
								else if(decOp->opcode == OP_calldelegate)
								{
									// Delegate
									RDX_PROTECT(ctx, SyncJournal(ctx) );

									{
										ILInstruction tickILI;
#ifdef RDX_ENABLE_TIMESLICE_COUNTER
									tickILI.opcode = ILOP_tick;
#else
									tickILI.opcode = ILOP_hardenstack;
#endif
										PUSH_INSTR(tickILI);
										meta->translatedInstrNum++;
									}

									if(!decOp->res)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(GCInfo::From(decOp->res)->containerType == objm->GetBuiltIns()->st_DelegateType)
									{
										dt = static_cast<DelegateType*>(decOp->res);
										params = dt->parameters;
										returnTypes = dt->returnTypes;
									}
									else
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								}
								else
									RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_GENERAL);

								LargeInt numParams = 0;
								if(params)
									numParams = GCInfo::From(params)->numElements;
								
								LargeInt paramLocation = 0;
								if(opstack->count)
									paramLocation = opstack->RetrieveTop(ctx, 0).offset;

								LargeInt thisLocation = 0;
								const Type *thisType = NULL;
								Int vftOffset = 0;
								bool taggedThis = false;

								VStackValue thisVst;
								
								LargeInt parameterOpstackUsed = 0;

								if(pass > PASS_CreateStacks)
								{
									for(LargeInt i=0;i<numParams;i++)
									{
										VStackValue paramVST;
										LargeInt paramStackOffset = numParams - 1 - i;
										RDX_PROTECT_ASSIGN(ctx, paramVST, opstack->RetrieveTop(ctx, paramStackOffset));

										if(params[i].isNotNull && !paramVST.isNotNull)
										{
											if(!objm->TypeIsObjectReference(params[i].type))
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											paramVST.notNullInstruction = instrNumIL;
											paramVST.isNotNull = true;
											RDX_PROTECT(ctx, opstack->ReinsertTop(ctx, paramStackOffset, paramVST));

											ILInstruction ili;
											ili.opcode = ILOP_verifynotnull;
											ILOpCompactArg opArg0, opArg1;
											opArg0.li = paramVST.offset;
											opArg1.li = i;
											PUSH_INSTR_CA_2(ili, opArg0, opArg1);

											meta->numValidationInstrs++;
										}
										
										// If the type isn't an exact match, enforce compatibility and demote

										// Explicit demotion is necessary because otherwise, a value could be implicitly demoted when
										// passed to a function, and then promoted by the function to a child value.  Because the journals
										// are still active in the parent frame, this can cause issues with the debug API and thread
										// deserialization.  We have to split the journal to prevent the value from being occupied earlier
										// with a weaker value earlier in its lifetime and then used by a different non-consuming operation
										// like OP_clone
										if(params[i].type != paramVST.vType && params[i].type != objm->GetBuiltIns()->st_Varying)
										{
											const GCInfo *paramType = GCInfo::From(params[i].type);
											const GCInfo *vstType = GCInfo::From(paramVST.vType);
											if(!objm->TypesCompatible(paramVST.vType, params[i].type))
												RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

											RDX_PROTECT(ctx, CallConvertOpstack(ctx, paramStackOffset, params[i].type));
										}
									}
								}

								bool paramsValidForTailCall = true;

								// Method signature must match, values must be of the expected type (i.e. POINTER to STRUCT is not allowed)
								for(LargeInt i=0;i<numParams;i++)
								{
									LargeInt pi = numParams - 1 - i;
									VSTType expectedType = VST_Value;
									if(params[pi].type == NULL)
										RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
									
									if(params[pi].type == objm->GetBuiltIns()->st_Varying)
										expectedType = VST_Varying;
									else if(TypeIsRefStruct(objm, params[pi].type))
									{
										if(params[pi].isConstant)
											expectedType = VST_ConstPointer;
										else
											expectedType = VST_Pointer;
									}

									VStackValue paramVST;
									RDX_PROTECT_ASSIGN(ctx, paramVST, PopOpstack(ctx, expectedType) );

									if(calledMethod && pi == (calledMethod->thisParameterOffset - 1))
									{

										thisLocation = paramVST.offset;
										thisType = paramVST.vType;
										taggedThis = true;
									}

									if(pass > PASS_CreateStacks)
									{
										if(expectedType != VST_Varying && paramVST.vType != params[pi].type)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										parameterOpstackUsed += paramVST.size;
									}

#if defined(RDX_JOURNAL_ALL_VALUES) && defined(RDX_TAIL_CALL_OPTIMIZATION)
									if(calledMethod == NULL || calledMethod != method)
										paramsValidForTailCall = false;
									else
									{
										// If all values are journaled, then tail callable values are limited to ones that will be valid in the calling frame
										// "this" parameter needs to be pass-through because the parent may have invoked this as a virtual call and replacing it could cause the call point validator to reject it
										// Reference structs need to be pass-through because they may be anchored to a local otherwise
										if(pi == method->thisParameterOffset - 1 || TypeIsRefStruct(objm, params[pi].type))
										{
											if(!paramVST.isLoadedFromLocal || paramVST.loadedLocal != pi)
												paramsValidForTailCall = false;
										}
									}
#endif
								}

								bool delegateFromParameter = false;
								LargeInt delegateSourceOffset = 0;
								if(decOp->opcode == OP_calldelegate)
								{
									VStackValue delegateVST;
									RDX_PROTECT_ASSIGN(ctx, delegateVST, PopOpstack(ctx, VST_LocalRef) );
									VLocal vl = localstack->RetrieveBottom(ctx, delegateVST.index);
									if(vl.isParameter)
									{
										delegateSourceOffset = vl.parameterOffset;
										delegateFromParameter = true;
										if(vl.isPointer)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									}
									else
									{
										delegateSourceOffset = vl.offset;
										delegateFromParameter = false;
									}
									if(pass > PASS_CreateStacks && vl.vType != decOp->res)
									{
										const GCInfo *vlInfo = GCInfo::From(vl.vType);
										const GCInfo *resInfo = GCInfo::From(decOp->res);
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									}
								}

								LargeInt rvLocation = -localOffsetMaximum;
								if(opstack->count)
									rvLocation = opstack->RetrieveTop(ctx, 0).offset;

								if(decOp->opcode == OP_call)
								{
									if(calledMethod->_native.isIntrinsic)
									{
										if(calledMethod->_native.isBranching)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										ILInstruction ili;
										ILOpCompactArg opArg0, opArg1, opArg2, opArg3;
										ili.opcode = calledMethod->_native.opcode;
										opArg0.li = paramLocation;
										opArg1.li = rvLocation;
										opArg2.li = calledMethod->_native.p2;
										opArg3.li = calledMethod->_native.p3;

										makeResumable = !calledMethod->_native.neverFails;
										PUSH_INSTR_CA_4(ili, opArg0, opArg1, opArg2, opArg3);
										makeResumable = false;
									}
									else
									{
										bool isTailCall = false;

#ifdef RDX_TAIL_CALL_OPTIMIZATION
										// See if this is a tail call
										// TODO: Optimize this into tail calls for other methods, also make it optional
										// If this is done, then care will be needed to ensure it doesn't cause thread serialization safety checks to fail
										// This can't be done inside of an exception handler
										if(paramsValidForTailCall && calledMethod == method && instrNum != (numDecodedInstructions-1) && decOps[instrNum+1].opcode == OP_return && instrNum > currentEHLastInstruction)
										{
											isTailCall = true;
											for(LargeInt i=0;i<numParams;i++)
											{
												const MethodParameter *methodParam = method->parameters + i;
												const Type *t = method->parameters[i].type;

												if(t == NULL)
													RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

												if(TypeIsRefStruct(objm, t))
												{
													isTailCall = false;
													break;
												}
											}
										}
#endif

										if(isTailCall)
										{
											{
												ILInstruction copyILI;
												ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
												copyILI.opcode = ILOP_move;
												opArg0.li = paramLocation;
												opArg1.li = -parameterOpstackUsed;
												opArg2.li = parameterOpstackUsed;
												opArg3.li = ALIGN_RuntimeStackValue;
												opArg4.li = ILOP_MOVE_DEST_PARENT_FRAME | ILOP_MOVE_SRC_TRANSIENT;

												PUSH_INSTR_CA_5(copyILI, opArg0, opArg1, opArg2, opArg3, opArg4);
											}

											// Go back to the function start
											ILInstruction ili;
											ili.opcode = ILCompiler::ILOP_jump;
											ILOpCompactArg opArg0;
											opArg0.li = 0;
											PUSH_INSTR_CA_1(ili, opArg0);
										}
										else
										{
											ILInstruction ili;
											ili.opcode = ILCompiler::ILOP_call;
											ILOpCompactArg opArg0, opArg1, opArg2;
											opArg0.li = (paramLocation & (~static_cast<LargeInt>(RDX_MAX_ALIGNMENT - 1)));
											opArg1.li = rvLocation;
											opArg2.p = calledMethod;
											PUSH_INSTR_CA_3(ili, opArg0, opArg1, opArg2);
										}
									}
								}
								else if(decOp->opcode == OP_callvirtual)
								{
									if(!taggedThis)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);


									if(thisType == NULL)
									{
										ILInstruction ili;
										ili.opcode = ILCompiler::ILOP_xnullref;
										PUSH_INSTR(ili);
									}
									else
									{
										if(GCInfo::From(thisType)->containerType != objm->GetBuiltIns()->st_StructuredType)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										const StructuredType *st = static_cast<const StructuredType*>(thisType);

										ILInstruction ili;
										ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4, opArg5;
										int argCount = 4;
										if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Class))
										{
											ili.opcode = ILCompiler::ILOP_callvirtual;
										}
										else if(st->storageSpecifier == EnumValue::From(StructuredType::SS_Interface))
										{
											// Make sure we can deduce this again in source expor
											if(calledMethod->parameters[calledMethod->thisParameterOffset - 1].type != thisType)
												RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_BAD_TYPE);

											ili.opcode = ILCompiler::ILOP_callinterface;
											opArg4.p = st;
											opArg5.p = calledMethod;
											argCount = 6;
										}
										else
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										// Can't call a non-virtual
										if(calledMethod->vftIndex <= 0)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										RDX_PROTECT(ctx, SyncJournal(ctx) );

										opArg0.li = (paramLocation & (~static_cast<LargeInt>(RDX_MAX_ALIGNMENT - 1)));
										opArg1.li = rvLocation;
										opArg2.li = thisLocation;
										opArg3.li = calledMethod->vftIndex - 1;

										if(argCount == 6)
											PUSH_INSTR_CA_6(ili, opArg0, opArg1, opArg2, opArg3, opArg4, opArg5);
										else if(argCount == 5)
											PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
										else
											PUSH_INSTR_CA_4(ili, opArg0, opArg1, opArg2, opArg3);
									}
								}
								else if(decOp->opcode == OP_calldelegate)
								{
									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1, opArg2;
									opArg0.li = (paramLocation & (~static_cast<LargeInt>(RDX_MAX_ALIGNMENT - 1)));
									opArg1.li = rvLocation;
									opArg2.li = delegateSourceOffset;
									if(delegateFromParameter)
										ili.opcode = ILCompiler::ILOP_calldelegateprv;
									else
										ili.opcode = ILCompiler::ILOP_calldelegatebp;
									PUSH_INSTR_CA_3(ili, opArg0, opArg1, opArg2);
								}


								// Clean up the stack and make sure the return values match
								if(pass > PASS_CreateStacks)
								{
									LargeInt numReturns = 0;
									if(returnTypes)
										numReturns = GCInfo::From(returnTypes)->numElements;

									for(LargeInt i=0;i<numReturns;i++)
									{
										LargeInt ri = numReturns - 1 - i;

										VStackValue vst;
										RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, i) );

										if(!objm->TypesCompatible(vst.vType, returnTypes[ri]))
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										if(vst.vType == NULL)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										if(TypeIsRefStruct(objm, vst.vType))
										{
											if(vst.vstType != VST_Pointer)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
											vst.vstType = VST_ConstPointer;
										}
										else
										{
											if(vst.vstType != VST_ValueShell)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										}

										RDX_PROTECT(ctx, LivenShell(ctx, vst) );
										RDX_PROTECT(ctx, opstack->ReinsertTop(ctx, i, vst) );
									}
								}
							}
							break;
						case OP_alloclocal:
							{
								// OPSTACK must be empty
								if(opstack->count)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

								Type *t = static_cast<Type*>(decOp->res);
								if(!t)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								if(!objm->ObjectCompatible(decOp->res, objm->GetBuiltIns()->st_Type))
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								if(t == objm->GetBuiltIns()->st_Varying)
									RDX_STHROW(ctx, Errors::RDX_ERROR_FORBIDDEN_VARYING_USAGE);

								LargeInt size, align;
								RDX_PROTECT(ctx, objm->TypeValueSize(ctx, t, size, align) );

								RDX_PROTECT(ctx, AppendLocal(ctx, t, instrNum, decOp->str, false) );

								LargeInt instanceStackLoc = localstack->RetrieveTop(ctx, 0).offset;

								if(!objm->TypeIsObjectReference(t) && GCInfo::From(t)->containerType == objm->GetBuiltIns()->st_StructuredType &&
										!(static_cast<const StructuredType*>(t)->_native.flags & StructuredType::NativeProperties::STF_ZeroFill))
								{
									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4, opArg5;
									ili.opcode = ILCompiler::ILOP_move;
									opArg0.p = static_cast<const StructuredType*>(t)->_native.actualDefaultValue;
									opArg1.li = instanceStackLoc;
									opArg2.li = size;
									opArg3.li = ALIGN_RuntimeStackValue;
									opArg4.li = ILOP_MOVE_SRC_TYPE_DEFAULT;
									opArg5.p = t;
									PUSH_INSTR_CA_6(ili, opArg0, opArg1, opArg2, opArg3, opArg4, opArg5);
								}
								else
								{
									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1;
									ili.opcode = ILCompiler::ILOP_zero;
									opArg0.li = instanceStackLoc;
									opArg1.li = size;
									PUSH_INSTR_CA_2(ili, opArg0, opArg1);
								}
							}
							break;
						case OP_createlocal:
							{
								// Type of the local and popped data must match exactly and it must be a value

								Type *t = static_cast<Type*>(decOp->res);
								if(!t || !objm->TypesCompatible(GCInfo::From(t)->containerType, objm->GetBuiltIns()->st_Type) )
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								if(t == objm->GetBuiltIns()->st_Varying)
									RDX_STHROW(ctx, Errors::RDX_ERROR_FORBIDDEN_VARYING_USAGE);
								
								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx, VST_Value) );
								RDX_PROTECT(ctx, AppendLocal(ctx, t, instrNum, decOp->str, false) );

								if(opstack->count != 0)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

								if(pass > PASS_CreateStacks)
								{
									if(vst.vType != t)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									LargeInt size, align;
									RDX_PROTECT(ctx, objm->TypeValueSize(ctx, t, size, align) );

									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
									ili.opcode = ILCompiler::ILOP_move;
									opArg0.li = vst.offset;
									opArg1.li = localstack->RetrieveTop(ctx, 0).offset;
									opArg2.li = size;
									opArg3.li = align;
									opArg4.li = ILOP_MOVE_SRC_TRANSIENT;

									PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
								}
							}
							break;
						case OP_removelocal:
							{
								if(opstack->count != 0)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);
								
								if(localstack->count == numMethodParameters)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_STACK_UNDERFLOW);

								VLocal vl;
								RDX_PROTECT_ASSIGN(ctx, vl, PopLocal(ctx) );
								
								if(pass > PASS_CreateStacks)
								{
									// Any instructions that exist while this local is active can only be jumped to within its lifespan
									for(LargeInt i=vl.creatingInstruction+1;i<instrNum;i++)
									{
										if(metalist[i].minJumpRange < vl.creatingInstruction)
											metalist[i].minJumpRange = vl.creatingInstruction;
										if(metalist[i].maxJumpRange > instrNum)
											metalist[i].maxJumpRange = instrNum;
									}
								}
							}
							break;
						case OP_pushempty:
							{
								VStackValue vst;
								vst.vType = static_cast<Type*>(decOp->res);
								if(!vst.vType || !objm->TypesCompatible(GCInfo::From(vst.vType)->containerType, objm->GetBuiltIns()->st_Type) )
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								if(TypeIsRefStruct(objm, vst.vType))
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								vst.vstType = VST_ValueShell;

								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );
							}
							break;
						case OP_newinstance:
							{
								RDX_PROTECT(ctx, SyncJournal(ctx) );

								VStackValue vst;
								vst.vstType = VST_Value;
								vst.vType = static_cast<Type*>(decOp->res);
								if(!vst.vType)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								LargeInt numDimensions = 0;
								Int expectedDimensions = decOp->int1;

								if(pass > PASS_CreateStacks)
								{
									const Type *containerType = GCInfo::From(vst.vType)->containerType;
									if(containerType == objm->GetBuiltIns()->st_StructuredType)
										numDimensions = 0;
									else if(containerType == objm->GetBuiltIns()->st_ArrayOfType)
										numDimensions = static_cast<const ArrayOfType*>(vst.vType)->numDimensions;
								}
								else
									numDimensions = expectedDimensions;

								if(numDimensions != expectedDimensions || numDimensions < 0)
									RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

								LargeInt dimensionLocation = 0;

								for(LargeInt i=0;i<numDimensions;i++)
								{
									VStackValue dimVst;
									RDX_PROTECT_ASSIGN(ctx, dimVst, PopOpstack(ctx, VST_Value) );
									if(pass > PASS_CreateStacks && dimVst.vType != objm->GetBuiltIns()->st_LargeInt)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(i == 0)
										dimensionLocation = dimVst.offset;
								}

								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );
								
								if(pass > PASS_CreateStacks)
								{
									LargeInt size, align;
									int argCount = 0;
									bool isStructuredType = (GCInfo::From(vst.vType)->containerType == objm->GetBuiltIns()->st_StructuredType);
									
									RDX_PROTECT(ctx, objm->TypeValueSize(ctx, vst.vType, size, align) );

									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4, opArg5;
									if(!objm->TypeIsObjectReference(vst.vType) && isStructuredType &&
										(static_cast<const StructuredType*>(vst.vType)->_native.flags & StructuredType::NativeProperties::STF_ZeroFill))
									{
										ili.opcode = ILCompiler::ILOP_zero;
										opArg0.li = vst.offset;
										opArg1.li = size;
										argCount = 2;
									}
									else
									{
										// Make sure this is a creatable type
										const void *instanceCopy = NULL;

										if(GCInfo::From(vst.vType)->containerType == objm->GetBuiltIns()->st_ArrayOfType)
										{
										}
										else if(isStructuredType)
										{
											const StructuredType* st = static_cast<const StructuredType*>(vst.vType);
											switch(st->storageSpecifier.Value())
											{
											case StructuredType::SS_Enum:
											case StructuredType::SS_Interface:
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
												break;
											case StructuredType::SS_Class:
												if(st->isAbstract)
													RDX_STHROW(ctx, Errors::RDX_ERROR_CREATED_ABSTRACT_TYPE);
												break;
											case StructuredType::SS_RefStruct:
											case StructuredType::SS_ValStruct:
												instanceCopy = st;
												break;
											default:
												RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_BAD_TYPE);
											}
										}
										else
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										
										if(instanceCopy)
										{
											ili.opcode = ILCompiler::ILOP_move;
											opArg0.p = static_cast<const StructuredType *>(instanceCopy)->_native.actualDefaultValue;
											opArg1.li = vst.offset;
											opArg2.li = vst.size;
											opArg3.li = ALIGN_RuntimeStackValue;
											opArg4.li = ILOP_MOVE_SRC_TYPE_DEFAULT;
											opArg5.p = instanceCopy;
											argCount = 6;
										}
										else
										{
											ili.opcode = ILCompiler::ILOP_newinstance;
											opArg0.li = vst.offset;
											opArg1.p = vst.vType;
											opArg2.li = dimensionLocation;
											opArg3.li = numDimensions;
											argCount = 4;
										}
									}

									if(argCount == 2)
										PUSH_INSTR_CA_2(ili, opArg0, opArg1);
									else if(argCount == 4)
										PUSH_INSTR_CA_4(ili, opArg0, opArg1, opArg2, opArg3);
									else if(argCount == 5)
										PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
									else if(argCount == 6)
										PUSH_INSTR_CA_6(ili, opArg0, opArg1, opArg2, opArg3, opArg4, opArg5);
								}
							}
							break;
						case OP_newinstanceset:
							{
								Type *iType = static_cast<Type*>(decOp->res);
								if(!iType)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								
								if(GCInfo::From(iType)->containerType != objm->GetBuiltIns()->st_StructuredType)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								StructuredType *st = static_cast<StructuredType *>(iType);
								if(objm->TypeIsObjectReference(iType))
								{
									// Might throw an exception, i.e. memory allocation failure
									RDX_PROTECT(ctx, SyncJournal(ctx) );
								}

								Int localIndex = decOp->int1;
								LargeInt numProperties = decOp->intVarCount;
								if(localIndex < 0 || localIndex >= localstack->count)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								
								bool isClass = false;
								switch(st->storageSpecifier.Value())
								{
								case StructuredType::SS_Enum:
								case StructuredType::SS_Interface:
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									break;
								case StructuredType::SS_Class:
									if(st->isAbstract)
										RDX_STHROW(ctx, Errors::RDX_ERROR_CREATED_ABSTRACT_TYPE);
									isClass = true;
									break;
								case StructuredType::SS_RefStruct:
								case StructuredType::SS_ValStruct:
									isClass = false;
									break;
								default:
									RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_BAD_TYPE);
								}

								// If this is a class, create and dispose of a pointer.  This isn't a recorded value, since the sets are all forbidden
								VStackValue pointerVst;
								pointerVst.vstType = VST_Pointer;
								pointerVst.vType = NULL;
								if(isClass)
								{
									RDX_PROTECT(ctx, AppendOpstack(ctx, pointerVst));
									RDX_PROTECT(ctx, PopOpstack(ctx, VST_Pointer));
								}


								if(pass > PASS_CreateStacks)
								{
									VLocal local;
									RDX_PROTECT_ASSIGN(ctx, local, localstack->RetrieveBottom(ctx, localIndex));

									if(!objm->TypesCompatible(st, local.vType))
										RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

									if(isClass)
									{
										ILOpCompactArg opArg0, opArg1, opArg2, opArg3;
										ILInstruction ili;
										ili.opcode = ILCompiler::ILOP_newinstance;
										opArg0.li = local.offset;
										opArg1.p = iType;
										opArg2.li = 0;
										opArg3.li = 0;
										PUSH_INSTR_CA_4(ili, opArg0, opArg1, opArg2, opArg3);
									}
									else
									{
										// Moves into the object aren't done using pins, which means they'll have aliasing problems with PCCM
										// Stack state needs to be flushed both before and after to allow aliasing
										ILInstruction ili;
										ili.opcode = ILCompiler::ILOP_hardenstack;
										PUSH_INSTR(ili);
									}

									// It's technically possible for a struct localref that's already been initialized to be clobbered by this
									// We don't care, since it's just as legal to set it up using a property init, and native unrecoverability is not guaranteed on structs

									LargeInt numTypeProperties = 0;
									if(st->properties)
										numTypeProperties = GCInfo::From(st->properties)->numElements;

									LargeInt lastOffset = 0;

									const UInt8 *argVar = decOp->intVarStart;
									LargeInt argVarBytes = decOp->intVarBytes;

									for(LargeInt i=0;i<decOp->intVarCount;i++)
									{
										// Determine what property this is
										Int propIndex;
										if(!DecodeInt<Int>(argVar, argVarBytes, propIndex))
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);
										if(propIndex < 0 || propIndex >= numTypeProperties)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										const Property *prop = st->properties + propIndex;
										bool expectPointer = TypeIsRefStruct(objm, prop->type);

										// Find the value
										VStackValue vst;
										RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, numProperties - 1 - i));

										if( (expectPointer && !VSTIsPointer(vst.vstType)) || (!expectPointer && vst.vstType != VST_Value))
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										if(!objm->TypesCompatible(vst.vType, prop->type))
											RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

										LargeInt vSize, vAlign;
										RDX_PROTECT(ctx, objm->TypeValueSize(ctx, prop->type, vSize, vAlign));

										LargeInt propOffset = st->_native.propertyOffsets[propIndex];
										if(isClass)
										{
											if(i == 0)
											{
												ILOpCompactArg opArg0, opArg1, opArg2;
												ILInstruction ili;
												ili.opcode = ILOP_objinterior_notnull_persist;
												opArg0.li = local.offset;
												opArg1.li = pointerVst.offset;
												opArg2.li = propOffset;
												PUSH_INSTR_CA_3(ili, opArg0, opArg1, opArg2);
											}
											else
											{
												ILOpCompactArg opArg0, opArg1;
												ILInstruction ili;
												ili.opcode = ILOP_incptr;
												opArg0.li = pointerVst.offset;
												opArg1.li = propOffset - lastOffset;
												PUSH_INSTR_CA_2(ili, opArg0, opArg1);
											}

											lastOffset = propOffset;

											{
												// Move
												ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
												ILInstruction ili;
												ili.opcode = ILOP_move;
												opArg0.li = vst.offset;
												opArg1.li = pointerVst.offset;
												opArg2.li = vSize;
												opArg3.li = vAlign;
												opArg4.li = (ILOP_MOVE_DEST_DEREF | ILOP_MOVE_DEST_OBJECT | ILOP_MOVE_SRC_TRANSIENT);
												if(expectPointer)
													opArg4.li |= ILOP_MOVE_SRC_DEREF;
												PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
											}
										}
										else
										{
											// Move
											ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
											ILInstruction ili;
											ili.opcode = ILOP_move;
											opArg0.li = vst.offset;
											opArg1.li = local.offset + propOffset;
											opArg2.li = vSize;
											opArg3.li = vAlign;
											opArg4.li = ILOP_MOVE_SRC_TRANSIENT;
											if(expectPointer)
												opArg4.li |= ILOP_MOVE_SRC_DEREF;
											PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
										}
									}

									// Done with properties

									// Flush the stack state if this is a non-class
									if(!isClass)
									{
										ILInstruction ili;
										ili.opcode = ILCompiler::ILOP_hardenstack;
										PUSH_INSTR(ili);
									}
								}

								for(LargeInt os=0;os<numProperties;os++)
								{
									RDX_PROTECT(ctx, PopOpstack(ctx, VST_Indeterminate));
								}
							}
							break;
						case OP_null:
							{
								VStackValue vst;
								vst.vstType = VST_Value;
								vst.vType = NULL;
								
								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

								ILInstruction ili;
								ILOpCompactArg opArg0, opArg1;
								ili.opcode = ILCompiler::ILOP_zero;
								opArg0.li = vst.offset;
								opArg1.li = sizeof(void*);

								PUSH_INSTR_CA_2(ili, opArg0, opArg1);
							}
							break;
						case OP_pinlocal:
							{
								// Local must be a struct and must not be a pointer (i.e. byref struct parameter)
								VStackValue lref;
								RDX_PROTECT_ASSIGN(ctx, lref, PopOpstack(ctx, VST_LocalRef) );

								VLocal vl = localstack->RetrieveBottom(ctx, lref.index);

								VStackValue vst;
								if(vl.isConstant)
									vst.vstType = VST_ConstPointer;
								else
									vst.vstType = VST_Pointer;

								vst.vType = vl.vType;
								vst.isFromLocal = true;
								if(vl.isParameter)
								{
									vst.pointerSourceType = Programmability::PST_PinnedParameter;
									vst.pointerSource = vl.parameterOffset;
								}
								else
								{
									vst.pointerSourceType = Programmability::PST_PinnedLocal;
									vst.pointerSource = vl.offset;
								}

								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

								if(pass > PASS_CreateStacks)
								{
									if(vl.isPointer)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1;

									if(vl.isParameter)
									{
										ili.opcode = ILCompiler::ILOP_pinp;
										opArg1.li = vl.parameterOffset;
									}
									else
									{
										ili.opcode = ILCompiler::ILOP_pinl;
										opArg1.li = vl.offset;
									}
									opArg0.li = vst.offset;
									PUSH_INSTR_CA_2(ili, opArg0, opArg1);
								}
							}
							break;
						case OP_tovarying:
							{
								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx, VST_Indeterminate) );

								VSTType originalVstType = vst.vstType;

								LargeInt ptrOriginalOffset = vst.offset;
								vst.vstType = VST_Varying;
								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

								if(pass > PASS_CreateStacks)
								{
									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1, opArg2;


									opArg0.li = vst.offset;
									opArg1.p = vst.vType;
									opArg2.li = ptrOriginalOffset;

									ILOpcode staticOp, movingOp;

									if(originalVstType == VST_Pointer || originalVstType == VST_ConstPointer)
									{
										staticOp = ILCompiler::ILOP_tovarying_static;
										movingOp = ILCompiler::ILOP_tovarying;
									}
									else
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(vst.offset != ptrOriginalOffset)
									{
										ili.opcode = movingOp;
										PUSH_INSTR_CA_3(ili, opArg0, opArg1, opArg2);
									}
									else
									{
										ili.opcode = staticOp;
										PUSH_INSTR_CA_2(ili, opArg0, opArg1);
									}
								}
							}
							break;
						case OP_arrayindex:
							{
								ILInstruction ili;
								ILOpCompactArg opArg0, opArg1, opArg2, opArg3;

								// Must be largeint list (size specified by int1) followed by an array with the same number of dimensions
								LargeInt numDimensions = decOp->int1;
								if(numDimensions < 1)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, 0) );

								RDX_PROTECT(ctx, SyncJournal(ctx) );

								ili.opcode = ILCompiler::ILOP_arrayindex;
								opArg0.li = vst.offset;

								for(LargeInt i=0;i<numDimensions;i++)
								{
									VStackValue dimVST;
									RDX_PROTECT_ASSIGN(ctx, dimVST, PopOpstack(ctx) );

									if(pass > PASS_CreateStacks)
									{
										if(dimVST.vType != objm->GetBuiltIns()->st_LargeInt)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									}
								}

								VStackValue arrayVST;
								VStackValue pointerVST;
								RDX_PROTECT_ASSIGN(ctx, arrayVST, PopOpstack(ctx) );

								opArg1.li = arrayVST.offset;

								if(pass > PASS_CreateStacks)
								{
									const Type *t = arrayVST.vType;
									const GCInfo *ti = GCInfo::From(t);
									if(!t || GCInfo::From(t)->containerType != objm->GetBuiltIns()->st_ArrayOfType)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									const ArrayOfType *aot = static_cast<const ArrayOfType*>(t);
									if(aot->numDimensions != numDimensions)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(aot->isConstant)
										pointerVST.vstType = VST_ConstPointer;
									else
										pointerVST.vstType = VST_Pointer;
									pointerVST.vType = aot->type;
								}

								
								RDX_PROTECT(ctx, AppendOpstack(ctx, pointerVST) );
								opArg2.li = opstack->RetrieveTop(ctx, 0).offset;

								opArg3.li = 0;	// Offset

								PUSH_INSTR_CA_4(ili, opArg0, opArg1, opArg2, opArg3);
							}
							break;
						case OP_property:
							{
								if(pass > PASS_CreateStacks)
								{
									VStackValue vst;
									RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, 0) );	// Retrieve so that the parameter is live when the journal syncs
									
									if(!VSTIsPointer(vst.vstType) &&
										vst.vstType != VST_Value)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(vst.vstType == VST_Value)
										RDX_PROTECT(ctx, SyncJournal(ctx) );

									// Pop for real now
									RDX_PROTECT(ctx, PopOpstack(ctx, vst.vstType) );

									// This value isn't pass-through any more
									vst.isLoadedFromLocal = false;

									const Type *t = vst.vType;
									if(t == NULL || GCInfo::From(t)->containerType != objm->GetBuiltIns()->st_StructuredType)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									const StructuredType *st = static_cast<const StructuredType*>(vst.vType);

									LargeInt numProperties = 0;
									if(st->properties)
										numProperties = GCInfo::From(st->properties)->numElements;

									if(decOp->int1 < 0 || decOp->int1 >= numProperties)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_PROPERTY);

									LargeInt paramOffset = st->_native.propertyOffsets[decOp->int1];
									Type *propertyType = st->properties[decOp->int1].type;

									if(!propertyType)
										RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

									if(VSTIsPointer(vst.vstType))
									{
										// Pointers to object references must be loaded first
										if(objm->TypeIsObjectReference(st))
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										if(st->properties[decOp->int1].isConstant)
											vst.vstType = VST_ConstPointer;
										vst.vType = propertyType;
										RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

										Opcode prevOpcode = decOps[instrNum-1].opcode;
										if(prevOpcode == OP_property ||
											prevOpcode == OP_pinlocal ||
											prevOpcode == OP_arrayindex)
										{
											// IL instructions emitted by any of these always have an offset in p3
											ILOpCompactArg offsetArg;
											RDX_PROTECT_ASSIGN(ctx, offsetArg, compactArgs->Pop(ctx) );
											offsetArg.li += paramOffset;
											RDX_PROTECT(ctx, compactArgs->Push(ctx, offsetArg) );
										}
										else
										{
											ILInstruction ili;
											ILOpCompactArg opArg0, opArg1;
											ili.opcode = ILCompiler::ILOP_incptr;
											opArg0.li = vst.offset;
											opArg1.li = paramOffset;
											PUSH_INSTR_CA_2(ili, opArg0, opArg1);
										}
									}
									else if(vst.vstType == VST_Value)
									{
										ILInstruction ili;
										ILOpCompactArg opArg0, opArg1, opArg2;
										ili.opcode = ILCompiler::ILOP_objinterior;
										opArg0.li = vst.offset;

										if(vst.isNotNull)
											ili.opcode = ILCompiler::ILOP_objinterior_notnull;

										RDX_PROTECT(ctx, SyncJournal(ctx) );

										if(st->storageSpecifier != EnumValue::From(StructuredType::SS_Class))
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										vst.vType = propertyType;

										if(st->properties[decOp->int1].isConstant)
											vst.vstType = VST_ConstPointer;
										else
											vst.vstType = VST_Pointer;
										vst.isFromLocal = false;
										vst.isLoadedFromLocal = false;
										RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

										opArg1.li = vst.offset;
										opArg2.li = paramOffset;

										PUSH_INSTR_CA_3(ili, opArg0, opArg1, opArg2);
									}
									else
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								}
							}
							break;
						case OP_move:
							{
								VStackValue destVst;
								VStackValue sourceVst;

								RDX_PROTECT_ASSIGN(ctx, destVst, PopOpstack(ctx, VST_Indeterminate) );
								RDX_PROTECT_ASSIGN(ctx, sourceVst, PopOpstack(ctx, VST_Indeterminate) );

								if(pass > PASS_CreateStacks)
								{
									const GCInfo *svi = GCInfo::From(sourceVst.vType);
									const GCInfo *dvi = GCInfo::From(destVst.vType);

									if(!objm->TypesCompatible(sourceVst.vType, destVst.vType))
										RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

									LargeInt size, align;
									RDX_PROTECT(ctx, objm->TypeValueSize(ctx, sourceVst.vType, size, align) );

									if(destVst.vstType == VST_Pointer)
									{
										ILInstruction ili;
										ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
										opArg1.li = destVst.offset;
										opArg2.li = size;
										opArg3.li = align;
										opArg4.li = ILCompiler::ILOP_MOVE_DEST_DEREF;

										if(!destVst.isFromLocal)
										{
											opArg4.li |= ILCompiler::ILOP_MOVE_DEST_OBJECT;
										}

										if(sourceVst.vstType == VST_Value)
										{
											opArg0.li = sourceVst.offset;
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
										}
										else if(VSTIsPointer(sourceVst.vstType))
										{
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_DEREF;
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
											if(!sourceVst.isFromLocal)
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_OBJECT;

											opArg0.li = sourceVst.offset;
										}
										else if(sourceVst.vstType == VST_LocalRef)
										{
											VLocal vl = localstack->RetrieveBottom(ctx, sourceVst.index);

											if(vl.isPointer)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											if(vl.isParameter)
											{
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_PARENT_FRAME;
												opArg0.li = vl.parameterOffset;
											}
											else
												opArg0.li = vl.offset;
										}
										else
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										ili.opcode = ILCompiler::ILOP_move;
										PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
									}
									else if(destVst.vstType == VST_LocalRef)
									{
										ILInstruction ili;
										ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
										bool destIsParameter;
										
										{
											VLocal vl = localstack->RetrieveBottom(ctx, destVst.index);

											if(vl.isPointer)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											destIsParameter = vl.isParameter;
											if(destIsParameter)
												opArg1.li = vl.parameterOffset;
											else
												opArg1.li = vl.offset;
										}
										
										opArg2.li = size;
										opArg3.li = align;
										opArg4.li = 0;
										
										if(destIsParameter)
											opArg4.li |= ILCompiler::ILOP_MOVE_DEST_PARENT_FRAME;

										if(sourceVst.vstType == VST_Value)
										{
											opArg0.li = sourceVst.offset;
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
										}
										else if(VSTIsPointer(sourceVst.vstType))
										{
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_DEREF;
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;

											opArg0.li = sourceVst.offset;
										}
										else if(sourceVst.vstType == VST_LocalRef)
										{
											VLocal vl = localstack->RetrieveBottom(ctx, sourceVst.index);

											if(vl.isPointer)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											if(vl.isParameter)
											{
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_PARENT_FRAME;
												opArg0.li = vl.parameterOffset;
											}
											else
											{
												opArg0.li = vl.offset;
											}
										}
										else
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										
										ili.opcode = ILCompiler::ILOP_move;
										PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
									}
									else
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								}
							}
							break;
						case OP_clone:
							{
								Int offset = decOp->int1;
								Int count = decOp->int2;

								if(offset < 0 || count < 1)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								if(offset >= opstack->count || (opstack->count - offset) < count)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_STACK_UNDERFLOW);

								LargeInt first = opstack->count - 1 - offset;
								LargeInt end = first + count;

								// Make sure none of these are shells and this doesn't cross a barrier boundary
								if(pass > PASS_CreateStacks)
								{
									for(LargeInt i=first;i<opstack->count;i++)
									{
										VStackValue vst;
										RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveBottom(ctx, i));
										if(vst.vstType == VST_Barrier)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_STACK_UNDERFLOW);
										if(vst.vstType == VST_ValueShell && i < end)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);
									}
								}

								for(LargeInt i=0;i<count;i++)
								{
									LargeInt vstIndex = first + i;
									VStackValue vst;
									RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveBottom(ctx, vstIndex));
									VStackValue clonedVst = vst;
									RDX_PROTECT(ctx, AppendOpstack(ctx, clonedVst) );

									if(pass > PASS_CreateStacks)
									{
										LargeInt size = 0;
										LargeInt align = 1;
										switch(vst.vstType)
										{
										case VST_Value:
											RDX_PROTECT(ctx, objm->TypeValueSize(ctx, vst.vType, size, align) );
											break;
										case VST_Pointer:
											size = sizeof(RuntimePointer<void>);
											align = ALIGN_RuntimeStackValue;
											break;
										case VST_Varying:
											size = sizeof(TypedRuntimePointer);
											align = ALIGN_RuntimeStackValue;
											break;
										case VST_LocalRef:
											size = align = 0;
											break;
										case VST_Barrier:
										case VST_ValueShell:
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
											break;
										}

										if(size)
										{
											ILInstruction ili;
											ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
											ili.opcode = ILCompiler::ILOP_move;
											opArg0.li = vst.offset;
											opArg1.li = clonedVst.offset;
											opArg2.li = size;
											opArg3.li = align;
											opArg4.li = 0;

											PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
										}
									}
								}
							}
							break;
						case OP_load:
							{
								VStackValue loadedVst;
								RDX_PROTECT_ASSIGN(ctx, loadedVst, PopOpstack(ctx, VST_Indeterminate) );

								if(pass > PASS_CreateStacks)
								{
									switch(loadedVst.vstType)
									{
									case VST_ConstPointer:
									case VST_Pointer:
										{
											LargeInt size, align;
											RDX_PROTECT(ctx, objm->TypeValueSize(ctx, loadedVst.vType, size, align) );

											if(TypeIsRefStruct(objm, loadedVst.vType))
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											VStackValue createdVst;
											createdVst.vType = loadedVst.vType;
											createdVst.vstType = VST_Value;

											RDX_PROTECT(ctx, AppendOpstack(ctx, createdVst) );

											ILInstruction ili;
											ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
											ili.opcode = ILCompiler::ILOP_move;
											opArg0.li = loadedVst.offset;
											opArg1.li = createdVst.offset;
											opArg2.li = size;
											opArg3.li = align;
											opArg4.li = 0;
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_DEREF;
											opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
											if(!loadedVst.isFromLocal)
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_OBJECT;

											PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
										}
										break;
									case VST_LocalRef:
										{
											VLocal vl;
											RDX_PROTECT_ASSIGN(ctx, vl, localstack->RetrieveBottom(ctx, loadedVst.index) );
											if(vl.isParameter)
											{
												LargeInt size, align;
												VStackValue createdVst;
												createdVst.vType = vl.vType;
												createdVst.isLoadedFromLocal = true;
												createdVst.loadedLocal = loadedVst.index;

												if(vl.isPointer)
												{
													if(vl.isConstant)
														createdVst.vstType = VST_ConstPointer;
													else
														createdVst.vstType = VST_Pointer;

													createdVst.pointerSource = vl.parameterOffset;
													createdVst.pointerSourceType = Programmability::PST_OffsetParameter;
													size = sizeof(RuntimePointer<void>);
													align = ALIGN_RuntimePointer;
													RDX_PROTECT(ctx, AppendOpstack(ctx, createdVst) );
												}
												else
												{
													createdVst.vstType = VST_Value;
													createdVst.isNotNull = vl.isNotNull;
													RDX_PROTECT(ctx, objm->TypeValueSize(ctx, vl.vType, size, align) );
													RDX_PROTECT(ctx, AppendOpstack(ctx, createdVst) );
												}
												
												ILInstruction ili;
												ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
												ili.opcode = ILCompiler::ILOP_move;
												opArg0.li = vl.parameterOffset;
												opArg1.li = createdVst.offset;
												opArg2.li = size;
												opArg3.li = align;
												opArg4.li = 0;
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_PARENT_FRAME;
												
												PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
											}
											else
											{
												VStackValue createdVst;
												createdVst.vstType = VST_Value;
												createdVst.vType = vl.vType;
												createdVst.isNotNull = vl.isNotNull;
												createdVst.isLoadedFromLocal = true;
												createdVst.loadedLocal = loadedVst.index;
												RDX_PROTECT(ctx, AppendOpstack(ctx, createdVst) );

												LargeInt size, align;
												RDX_PROTECT(ctx, objm->TypeValueSize(ctx, vl.vType, size, align) );

												ILInstruction ili;
												ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
												ili.opcode = ILCompiler::ILOP_move;
												opArg0.li = vl.offset;
												opArg1.li = createdVst.offset;
												opArg2.li = size;
												opArg3.li = align;
												opArg4.li = 0;
												
												PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
											}
											
										}
										break;
									default:
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									};

								}
								else
								{
									VStackValue createdVst;
									RDX_PROTECT(ctx, AppendOpstack(ctx, createdVst) );
								}
							}
							break;
						case OP_pop:
							{
								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx, VST_Indeterminate) );

								if(pass > PASS_CreateStacks)
								{
									switch(vst.vstType)
									{
									case VST_Value:
									case VST_Pointer:
									case VST_ConstPointer:
									case VST_Varying:
									case VST_LocalRef:
									case VST_ValueShell:
										// Valid thing to pop
										break;
									default:
										{
											// Invalid (i.e. a barrier)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										}
										break;
									};
								}
							}
							break;
						case OP_cast:
							{
								RDX_PROTECT(ctx, SyncJournal(ctx) );

								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx, VST_Value) );

								if(pass > PASS_CreateStacks)
								{
									Type *t = static_cast<Type*>(decOp->res);
									if(!t || !objm->TypeIsValid(t))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									const Char *fromType = GCInfo::From(vst.vType)->gstSymbol->AsChars();
									const Char *toType = GCInfo::From(t)->gstSymbol->AsChars();

									if(!objm->TypesCompatible(vst.vType, t))
									{
										// This will require a polymorphic cast
										if(!objm->TypesCompatiblePolymorphic(vst.vType, t))
											RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

										if(GCInfo::From(t)->containerType == objm->GetBuiltIns()->st_StructuredType &&
											static_cast<const StructuredType *>(t)->storageSpecifier == EnumValue::From(StructuredType::SS_Enum))
										{
											ILInstruction ili;
											ILOpCompactArg opArg0, opArg1;
											ili.opcode = ILCompiler::ILOP_assertenum;
											opArg0.li = vst.offset;
											opArg1.p = t;

											PUSH_INSTR_CA_2(ili, opArg0, opArg1);
										}
										else if(objm->TypeIsObjectReference(t))
										{
											ILInstruction ili;
											ILOpCompactArg opArg0, opArg1;
											ili.opcode = ILCompiler::ILOP_assertinherits;
											opArg0.li = vst.offset;
											opArg1.p = t;

											PUSH_INSTR_CA_2(ili, opArg0, opArg1);
										}
										else
											RDX_STHROW(ctx, Errors::RDX_ERROR_INTERNAL_BAD_TYPE);
									}

									vst.vType = t;
								}

								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );
							}
							break;
						case OP_localref:
							{
								VStackValue vst;
								vst.vstType = VST_LocalRef;
								vst.index = decOp->int1;

								if(decOp->int1 < 0 || decOp->int1 >= localstack->count)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								vst.vType = localstack->RetrieveBottom(ctx, decOp->int1).vType;
								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );
							}
							break;
						case OP_return:
							{
								// Opstack must match the expected return types.
								// Must be followed by a barrier or the end of the stack
								// Return values may be pointers, localrefs, or objectrefs, but must not require conversion to the destination types.

								LargeInt numReturnVals = decOp->int1;
								if(numReturnVals < 0)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								if(pass > PASS_CreateStacks)
								{
									if(numReturnVals == opstack->count)
									{
										Type **returnTypes = method->returnTypes;
										// Returning to the parent frame
										LargeInt numMethodReturns = 0;
										if(returnTypes)
											numMethodReturns = GCInfo::From(returnTypes)->numElements;

										if(numReturnVals != numMethodReturns)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										LargeInt destinationOffset = 0;
										for(LargeInt i=0;i<numReturnVals;i++)
										{
											VStackValue valueVst;
											Type *destinationReturnType;

											RDX_PROTECT_ASSIGN(ctx, valueVst, PopOpstack(ctx, VST_Indeterminate) );
											destinationReturnType = returnTypes[numReturnVals-1-i];

											if(valueVst.vstType != VST_LocalRef &&
												valueVst.vstType != VST_Value &&
												!VSTIsPointer(valueVst.vstType))
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											bool isRefStruct = TypeIsRefStruct(objm, valueVst.vType);
											if( !objm->TypesCompatible(valueVst.vType, destinationReturnType))
												RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

											LargeInt size, align;
											RDX_PROTECT(ctx, objm->TypeValueSize(ctx, valueVst.vType, size, align) );

											ILInstruction ili;
											ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;

											ili.opcode = ILCompiler::ILOP_move;
											opArg4.li = 0;
											opArg4.li |= ILCompiler::ILOP_MOVE_DEST_PARENT_FRAME;

											if(valueVst.vstType == VST_LocalRef)
											{
												VLocal vl;
												vl = localstack->RetrieveBottom(ctx, valueVst.index);
												if(vl.isParameter)
												{
													opArg4.li |= ILCompiler::ILOP_MOVE_SRC_PARENT_FRAME;

													if(vl.isPointer)
													{
														opArg4.li |= ILCompiler::ILOP_MOVE_SRC_DEREF;
														opArg4.li |= ILCompiler::ILOP_MOVE_DEST_DEREF;
													}

													opArg0.li = vl.parameterOffset;
													opArg1.li = destinationOffset;
												}
												else
												{
													if(isRefStruct)
														opArg4.li |= ILCompiler::ILOP_MOVE_DEST_DEREF;

													opArg0.li = vl.offset;
												}
												opArg1.li = destinationOffset;
											}
											else if(VSTIsPointer(valueVst.vstType))
											{
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_DEREF;
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
												if(isRefStruct)
													opArg4.li |= ILCompiler::ILOP_MOVE_DEST_DEREF;
												if(!valueVst.isFromLocal)
													opArg4.li |= ILCompiler::ILOP_MOVE_SRC_OBJECT;
												opArg0.li = valueVst.offset;
												opArg1.li = destinationOffset;
											}
											else if(valueVst.vstType == VST_Value)
											{
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
												opArg0.li = valueVst.offset;
												opArg1.li = destinationOffset;
											}
											opArg2.li = size;
											opArg3.li = align;

											PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);

											LargeInt sizeUsed;
											VSTType vstType = VST_Value;
											if(TypeIsRefStruct(objm, destinationReturnType))
												vstType = VST_Pointer;

											RDX_PROTECT_ASSIGN(ctx, sizeUsed, OpstackUsed(ctx, vstType, destinationReturnType) );
											
											if(!CheckAddOverflow(destinationOffset, sizeUsed))
												RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);
											destinationOffset += sizeUsed;
										}
										
										{
											ILInstruction ili;
											ili.opcode = ILCompiler::ILOP_exit;
											PUSH_INSTR(ili);
										}
									}
									else
									{
										// Returning past a barrier in the current frame
										if(numReturnVals < 1 || numReturnVals > opstack->count)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										LargeInt targetInstruction;
										LargeInt barrierCount = 1;
										for(targetInstruction=instrNum;targetInstruction<numDecodedInstructions;targetInstruction++)
										{
											if(decOps[targetInstruction].opcode == OP_endbarrier)
												barrierCount--;
											else if(decOps[targetInstruction].opcode == OP_startbarrier)
												barrierCount++;
											if(!barrierCount)
												break;
										}

										if(barrierCount != 0)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

										// Make sure the barrier matches
										VStackValue vst;
										RDX_PROTECT_ASSIGN(ctx, vst, opstack->RetrieveTop(ctx, numReturnVals) );

										if(vst.vstType != VST_Barrier || vst.barrierValueCount != numReturnVals)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										for(LargeInt i=0;i<numReturnVals;i++)
										{
											VStackValue valueVst;
											VStackValue destinationVst;
											RDX_PROTECT_ASSIGN(ctx, valueVst, PopOpstack(ctx, VST_Indeterminate) );
											RDX_PROTECT_ASSIGN(ctx, destinationVst, opstack->RetrieveTop(ctx, numReturnVals) );		// This slides due to the above pop

											if(valueVst.vstType != VST_LocalRef &&
												valueVst.vstType != VST_Value &&
												!VSTIsPointer(valueVst.vstType))
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											bool isRefStruct = TypeIsRefStruct(objm, valueVst.vType);
											if( (isRefStruct && destinationVst.vstType != VST_Pointer) ||
												(!isRefStruct && destinationVst.vstType != VST_ValueShell) ||
												!objm->TypesCompatible(valueVst.vType, destinationVst.vType))
												RDX_STHROW(ctx, Errors::RDX_ERROR_INCOMPATIBLE_CONVERSION);

											LargeInt size, align;
											RDX_PROTECT(ctx, objm->TypeValueSize(ctx, valueVst.vType, size, align) );

											if(destinationVst.vstType == VST_ValueShell)
												RDX_PROTECT(ctx, LivenShell(ctx, destinationVst) );

											ILInstruction ili;
											ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;
											opArg4.li = 0;
											ili.opcode = ILCompiler::ILOP_move;

											if(valueVst.vstType == VST_LocalRef)
											{
												VLocal vl;
												vl = localstack->RetrieveBottom(ctx, valueVst.index);
												if(vl.isParameter)
												{
													opArg4.li |= ILCompiler::ILOP_MOVE_SRC_PARENT_FRAME;

													if(vl.isPointer)
													{
														opArg4.li |= ILCompiler::ILOP_MOVE_SRC_DEREF;
														opArg4.li |= ILCompiler::ILOP_MOVE_DEST_DEREF;
													}

													opArg0.li = vl.parameterOffset;
													opArg1.li = destinationVst.offset;
												}
												else
												{
													if(isRefStruct)
														opArg4.li |= ILCompiler::ILOP_MOVE_DEST_DEREF;

													opArg0.li = vl.offset;
													opArg1.li = destinationVst.offset;
												}
											}
											else if(valueVst.vstType == VST_Pointer)
											{
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_DEREF;
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
												if(isRefStruct)
													opArg4.li |= ILCompiler::ILOP_MOVE_DEST_DEREF;
												opArg0.li = valueVst.offset;
												opArg1.li = destinationVst.offset;
											}
											else if(valueVst.vstType == VST_Value)
											{
												opArg4.li |= ILCompiler::ILOP_MOVE_SRC_TRANSIENT;
												opArg0.li = valueVst.offset;
												opArg1.li = destinationVst.offset;
											}
											opArg2.li = size;
											opArg3.li = align;

											PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
										}

										{
											ILInstruction ili;
											ILOpCompactArg opArg0;
											ili.opcode = ILCompiler::ILOP_jump;
											RDX_PROTECT_ASSIGN(ctx, opArg0.li, StitchJump(ctx, instrNum, targetInstruction) );

											PUSH_INSTR_CA_1(ili, opArg0);
										}
									}
								}
								else
								{
									for(LargeInt i=0;i<numReturnVals;i++)
										RDX_PROTECT(ctx, PopOpstack(ctx) );
								}
							}
							break;
						case OP_hash:
							{
								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx, VST_Indeterminate) );
								LargeInt hashSource = vst.offset;
								LargeInt size, align;
								bool expectRef = false;

								size = align = 0;
								if(pass > PASS_CreateStacks)
								{
									if(vst.vstType != VST_Value && vst.vstType != VST_Pointer)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									expectRef = TypeIsRefStruct(objm, vst.vType);
									if(expectRef && (vst.vstType != VST_Pointer))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									if(!expectRef && (vst.vstType != VST_Value))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									RDX_PROTECT(ctx, objm->TypeValueSize(ctx, vst.vType, size, align) );
								}

								vst.vstType = VST_Value;
								vst.vType = st_HashCode;

								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );


								ILInstruction ili;
								ILOpCompactArg opArg0, opArg1, opArg2, opArg3;
								if(expectRef)
									ili.opcode = ILCompiler::ILOP_hash_p;
								else
									ili.opcode = ILCompiler::ILOP_hash_f;

								opArg0.li = hashSource;
								opArg1.li = vst.offset;
								opArg2.li = size;
								opArg3.li = align;

								PUSH_INSTR_CA_4(ili, opArg0, opArg1, opArg2, opArg3);
							}
							break;
						case OP_res:
							{
								VStackValue vst;
								ILInstruction ili;
								ILOpLargeArg opArg0, opArg1;

								void *res = decOp->res;
								if(!res)
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

								const Type *resType = GCInfo::From(res)->containerType;
								const Type *resTypeType = GCInfo::From(resType)->containerType;

								if(resTypeType == objm->GetBuiltIns()->st_StructuredType)
								{
									switch(static_cast<const StructuredType*>(resType)->storageSpecifier.Value())
									{
									case StructuredType::SS_RefStruct:
									case StructuredType::SS_ValStruct:
									case StructuredType::SS_Enum:
										{
											vst.vstType = VST_Pointer;
											vst.vType = resType;

											RuntimePointer<void> rtp;

											rtp.objectRef = res;
											rtp.valueRef = res;

											if(GCInfo::From(res)->gcflags & GCInfo::GCOF_ConstantStructure)
												vst.vstType = VST_ConstPointer;

											ili.opcode = ILCompiler::ILOP_immediate_rtp;
											opArg0.rtp.objectRef = res;
											opArg0.rtp.valueRef = res;
											opArg1.ca[1].li = sizeof(RuntimePointer<void>);
										}
										break;
									case StructuredType::SS_Interface:
									case StructuredType::SS_Class:
										{
											vst.vstType = VST_Value;
											vst.vType = resType;
									
											ili.opcode = ILCompiler::ILOP_immediate_ptr;
											opArg0.ca[0].p = res;
											opArg1.ca[1].li = sizeof(void*);
										}
										break;
									default:
										RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
									};
								}
								else if(resTypeType == objm->GetBuiltIns()->st_ArrayOfType)
								{
									vst.vstType = VST_Value;
									vst.vType = resType;
									
									ili.opcode = ILCompiler::ILOP_immediate_ptr;
									opArg0.ca[0].p = res;
									opArg1.ca[1].li = sizeof(void*);

								}
								else
									RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
								
								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );
								
								opArg1.ca[0].li = vst.offset;
								PUSH_INSTR_LA_2(ili, opArg0, opArg1);
							}
							break;
						case OP_constant:
							{
								VStackValue vst;
								vst.vstType = VST_Value;
								vst.vType = static_cast<Type*>(decOp->res);
								if(!vst.vType || !objm->TypesCompatible(GCInfo::From(vst.vType)->containerType, objm->GetBuiltIns()->st_Type) )
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								
								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

								if(pass > PASS_CreateStacks)
								{
									ILInstruction ili;
									ILOpLargeArg opArg0, opArg1;
									ili.opcode = ILCompiler::ILOP_immediate;
									opArg1.ca[0].li = vst.offset;

									HugeInt hi1 = decOp->hi1;

									if(!vst.vType)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									const String *typeName = GCInfo::From(vst.vType)->gstSymbol;
									if(!typeName)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									if(GCInfo::From(vst.vType)->containerType == objm->GetBuiltIns()->st_StructuredType &&
										static_cast<const StructuredType *>(vst.vType)->storageSpecifier == EnumValue::From(StructuredType::SS_Enum))
									{
										DECODE_INTEGER(EnumValue::UnderlyingType, opArg0.ca[0].evu);
										if(!objm->EnumCompatible(opArg0.ca[0].ev, static_cast<const StructuredType *>(vst.vType)->enumerants))
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										opArg1.ca[1].lui = sizeof(EnumValue);
									}
									else if(typeName->Equal("Core.int"))
									{
										DECODE_INTEGER(Int, opArg0.ca[0].si);
										opArg1.ca[1].lui = sizeof(Int);
									}
									else if(typeName->Equal("Core.byte"))
									{
										DECODE_INTEGER(Byte, opArg0.ca[0].b);
										opArg1.ca[1].lui = sizeof(Byte);
									}
									else if(typeName->Equal("Core.largeint"))
									{
										DECODE_INTEGER(LargeInt, opArg0.ca[0].li);
										opArg1.ca[1].lui = sizeof(LargeInt);
									}
									else if(typeName->Equal("Core.float"))
									{
										DECODE_FLOAT(Float, opArg0.ca[0].f);
										opArg1.ca[1].lui = sizeof(Float);
									}
									else if(typeName->Equal("Core.double"))
									{
										DECODE_FLOAT(Double, opArg0.dbl);
										opArg1.ca[1].lui = sizeof(Double);
									}
									else if(typeName->Equal("Core.bool"))
									{
										Int bVal;
										DECODE_INTEGER(Int, bVal);
										if(bVal != 0)
											opArg0.ca[0].bo = TrueValue;
										else
											opArg0.ca[0].bo = FalseValue;
										opArg1.ca[1].lui = sizeof(Bool);
									}
									else
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									PUSH_INSTR_LA_2(ili, opArg0, opArg1);
								}
							}
							break;
						case OP_constant_str:
							{
								VStackValue vst;
								vst.vstType = VST_Value;
								vst.vType = static_cast<Type*>(decOp->res);
								if(!vst.vType || !objm->TypesCompatible(GCInfo::From(vst.vType)->containerType, objm->GetBuiltIns()->st_Type) )
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
								
								RDX_PROTECT(ctx, AppendOpstack(ctx, vst) );

								if(pass > PASS_CreateStacks)
								{
									ILInstruction ili;
									ILOpLargeArg opArg0, opArg1;
									ili.opcode = ILCompiler::ILOP_immediate;
									opArg1.ca[0].li = vst.offset;

									const String *typeName = GCInfo::From(vst.vType)->gstSymbol;
									const String *ivalue = decOp->str;
									if(!typeName)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									if(typeName->Equal("Core.string"))
									{
										ili.opcode = ILCompiler::ILOP_immediate_ptr;
										if(!ivalue)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										opArg0.ca[0].p = ivalue;
										opArg1.ca[1].lui = sizeof(String*);
									}
									else
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									PUSH_INSTR_LA_2(ili, opArg0, opArg1);
								}
							}
							break;
						case OP_switch:
							{
								VStackValue vst;
								RDX_PROTECT_ASSIGN(ctx, vst, PopOpstack(ctx, VST_Indeterminate) );

								if(pass > PASS_CreateStacks)
								{
									VSTType valueVSTType = vst.vstType;
									if(valueVSTType != VST_Value && !VSTIsPointer(valueVSTType))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(opstack->count != 0 && opstack->RetrieveTop(ctx, 0).vstType != VST_Barrier)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

									if(vst.vType == NULL)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									const void *searchArray = decOp->res;
									LargeInt numCases = decOp->int1;

									if(searchArray == NULL)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									
									if((GCInfo::From(searchArray)->gcflags & GCInfo::GCOF_ConstantArray) == 0)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									// Make sure that the array is of the value type
									const GCInfo *searchArrayGCI = GCInfo::From(searchArray);
									if(searchArrayGCI->containerType == NULL || GCInfo::From(searchArrayGCI->containerType)->containerType != objm->GetBuiltIns()->st_ArrayOfType)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									if(searchArrayGCI->numElements != numCases)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
									if(!objm->TypesCompatible(vst.vType, searchArrayGCI->contentsType))
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									LargeInt size, align;
									RDX_PROTECT(ctx, objm->TypeValueSize(ctx, vst.vType, size, align) );

									if(numCases <= 0)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									// Make sure that there's an instruction after the case jumps
									if(pass > PASS_CreateStacks && numDecodedInstructions - instrNum - 2 < numCases)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									// Make sure that the next numCases instructions are all jumps that jump forward
									{
										for(LargeInt i=0;i<numCases;i++)
										{
											LargeInt caseInstrNum = instrNum + i + 1;
											if(caseInstrNum == numDecodedInstructions)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

											if(decOps[caseInstrNum].opcode != OP_jump || decOps[caseInstrNum].int1 <= 0)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										}
									}

									ILInstruction ili;
									ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4;

									if(VSTIsPointer(valueVSTType))
										ili.opcode = ILCompiler::ILOP_switch_ptr;
									else
										ili.opcode = ILCompiler::ILOP_switch;
									opArg0.li = vst.offset;
									opArg1.p = decOp->res;
									opArg2.li = decOp->int1;
									opArg3.li = size;
									opArg4.li = align;
									
									PUSH_INSTR_CA_5(ili, opArg0, opArg1, opArg2, opArg3, opArg4);
								}
							}
							break;
						case OP_iteratearray:
							{
								RDX_PROTECT(ctx, SyncJournal(ctx) );

								if(decOp->int1 <= 0)
								{
									ILInstruction tickILI;
#ifdef RDX_ENABLE_TIMESLICE_COUNTER
									tickILI.opcode = ILOP_tick;
#else
									tickILI.opcode = ILOP_hardenstack;
#endif
									PUSH_INSTR(tickILI);
									meta->translatedInstrNum++;
								}
								VStackValue arrayVst;
								VStackValue indexVst;
								VStackValue destVst;
								VStackValue subIndexVst;
								RDX_PROTECT_ASSIGN(ctx, arrayVst, PopOpstack(ctx, VST_LocalRef) );
								RDX_PROTECT_ASSIGN(ctx, indexVst, PopOpstack(ctx, VST_LocalRef) );
								RDX_PROTECT_ASSIGN(ctx, destVst, PopOpstack(ctx, VST_LocalRef) );

								if(decOp->int2 != 0)
								{
									RDX_PROTECT_ASSIGN(ctx, subIndexVst, PopOpstack(ctx, VST_LocalRef) );
								}
	
								if(pass > PASS_CreateStacks)
								{
									if(opstack->count != 0 && opstack->RetrieveTop(ctx, 0).vstType != VST_Barrier)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE);

									VLocal arrayLocal = localstack->RetrieveBottom(ctx, arrayVst.index);
									VLocal indexLocal = localstack->RetrieveBottom(ctx, indexVst.index);
									VLocal destLocal = localstack->RetrieveBottom(ctx, destVst.index);
									VLocal subIndexLocal;

									if(arrayLocal.isParameter || arrayLocal.vType == NULL ||
										GCInfo::From(arrayLocal.vType)->containerType != objm->GetBuiltIns()->st_ArrayOfType)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									const ArrayOfType *arrayLocalType = static_cast<const ArrayOfType *>(arrayLocal.vType);

									if(indexLocal.isParameter || indexVst.vType != objm->GetBuiltIns()->st_LargeInt)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									if(decOp->int2 != 0)
									{
										if(decOp->int2 < 0)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										if(static_cast<const ArrayOfType*>(arrayLocal.vType)->numDimensions != decOp->int2)
											RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

										 subIndexLocal = localstack->RetrieveBottom(ctx, subIndexVst.index);

										// Make sure each subindex local is a largeint
										for(Int i=0;i<decOp->int2;i++)
										{
											VLocal checkLocal;
											RDX_PROTECT_ASSIGN(ctx, checkLocal, localstack->RetrieveBottom(ctx, subIndexVst.index + static_cast<LargeInt>(i)) );
											if(checkLocal.isParameter || checkLocal.vType != objm->GetBuiltIns()->st_LargeInt)
												RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
										}
									}

									if(destLocal.isParameter || destVst.vType == NULL || destVst.vType != arrayLocalType->type)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									LargeInt targetInstruction = instrNum + decOp->int1;
									if(targetInstruction < 0 || targetInstruction >= numDecodedInstructions)
										RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

									ILInstruction ili;

									ILOpCompactArg opArg0, opArg1, opArg2, opArg3, opArg4, opArg5, opArg6, opArg7;
									opArg0.li = arrayLocal.offset;
									opArg1.li = indexLocal.offset;
									opArg2.li = destLocal.offset;
									RDX_PROTECT_ASSIGN(ctx, opArg3.li, StitchJump(ctx, instrNum, targetInstruction) );

									RDX_PROTECT(ctx, objm->TypeValueSize(ctx, destVst.vType, opArg4.li, opArg5.li) );
									
									if(decOp->int2 == 0)
									{
										ili.opcode = ILCompiler::ILOP_iteratearray;
										PUSH_INSTR_CA_6(ili, opArg0, opArg1, opArg2, opArg3, opArg4, opArg5);
									}
									else
									{
										ili.opcode = ILCompiler::ILOP_iteratearraysub;
										opArg6.li = subIndexLocal.offset;
										opArg7.li = decOp->int2;
										PUSH_INSTR_CA_8(ili, opArg0, opArg1, opArg2, opArg3, opArg4, opArg5, opArg6, opArg7);
									}
								}
							}
							break;
						default:
							RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
						};
					}

					// If this triggers and stackjournal->count != numMethodParameters, then the opstack was probably manipulated with
					// direct access instead of AppendOpstack and PopOpstack
					if(opstack->count != 0 || localstack->count != numMethodParameters || stackjournal->count != numMethodParameters)
						RDX_STHROW(ctx, Errors::RDX_ERROR_IL_STACKS_UNBALANCED);

					ILInstruction ili;
					ili.opcode = ILCompiler::ILOP_fatal;
					PUSH_INSTR(ili);

					FLUSH_RESUME_FLAGS;

					localstack->count = 0;
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROW(ctx);
				}
				RDX_ENDTRY
			}

			static void DecodeOps(OperationContext *ctx, Method *m, IObjectManager *objm, ILDecodedOp *decOps)
			{
				RDX_TRY(ctx)
				{
					LargeInt bytesRemaining = GCInfo::From(m->bytecode)->numElements;
					LargeInt numInstructions = m->numInstructions;
					const UInt8 *bytecode = m->bytecode;

					for(LargeInt i=0;i<numInstructions;i++)
					{
						ILDecodedOp decOp;
						memset(&decOp, 0, sizeof(decOp));

						Int opcode;
						if(!DecodeInt<Int>(bytecode, bytesRemaining, opcode))
							RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);
						decOp.opcode = static_cast<Opcode>(opcode);

						if(opcode < 0 || opcode >= OP_Count)
							RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPCODE);

						// Decode arguments
						int argParamFlags = OPCODE_INFO[opcode].paramFlags;

						if(argParamFlags & OpArgs::INT1)
						{
							if(!DecodeInt<Int64>(bytecode, bytesRemaining, decOp.hi1))
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);
							decOp.int1 = static_cast<Int>(decOp.hi1);
						}

						if(argParamFlags & OpArgs::INT2)
						{
							if(!DecodeInt<Int>(bytecode, bytesRemaining, decOp.int2))
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);
						}

						if(argParamFlags & OpArgs::INTVAR)
						{
							if(!DecodeInt(bytecode, bytesRemaining, decOp.intVarCount))
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);
							if(decOp.intVarCount < 0)
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
							decOp.intVarStart = bytecode;

							Int dummy;
							for(Int vi=0;vi<decOp.intVarCount;vi++)
								if(!DecodeInt(bytecode, bytesRemaining, dummy))
									RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);

							decOp.intVarBytes = bytecode - reinterpret_cast<const UInt8 *>(decOp.intVarStart);
						}
						if(argParamFlags & OpArgs::STR)
						{
							Int resIndex;
							if(!DecodeInt(bytecode, bytesRemaining, resIndex))
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);
							if(resIndex < 0 || m->resArgs == NULL || resIndex >= GCInfo::From(m->resArgs)->numElements)
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
							void *strResArg = m->resArgs[resIndex];
							if(strResArg != NULL && GCInfo::From(strResArg)->containerType != objm->GetBuiltIns()->st_String)
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
							decOp.str = static_cast<const String *>(strResArg);
						}
						if(argParamFlags & OpArgs::RES)
						{
							Int resIndex;
							if(!DecodeInt(bytecode, bytesRemaining, resIndex))
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_ARGUMENT_UNDERRUN);
							if(resIndex < 0 || m->resArgs == NULL || resIndex >= GCInfo::From(m->resArgs)->numElements)
								RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
							decOp.res = m->resArgs[resIndex];
						}

						decOps[i] = decOp;
					}

					if(bytesRemaining)
						RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);
				}
				RDX_CATCH(ctx)
				{
					RDX_RETHROW(ctx);
				}
				RDX_ENDTRY
			}
		};

		void CompactJournals(OperationContext *ctx, Method *m, StackJournal *journals, LargeInt numJournals, IObjectManager *objm, LargeInt &outCompactSize, LargeInt &outNumDropped)
		{
			RDX_TRY(ctx)
			{
				// Determine what is traceable
				for(LargeInt j=0;j<numJournals;j++)
					journals[j].isTraceable = objm->TypeCanBeTraced(journals[j].vType);

				// Compact the journals
				LargeInt compactSize = 0;
				LargeInt numDroppedJournals = 0;
				for(LargeInt j=0;j<numJournals;j++)
				{
#ifndef RDX_JOURNAL_ALL_VALUES
					if(journals[j].isTraceable)
#endif
						compactSize += journals[j].Compress(NULL);
#ifndef RDX_JOURNAL_ALL_VALUES
					else
						numDroppedJournals++;
#endif
				}

				if(compactSize)
					RDX_PROTECT_ASSIGN(ctx, m->_native.compactedJournals, objm->Create1DArray<UInt8>(ctx, compactSize) );
				UInt8 *outp = m->_native.compactedJournals;

				for(LargeInt j=0;j<numJournals;j++)
				{
#ifndef RDX_JOURNAL_ALL_VALUES
					if(journals[j].isTraceable)
#endif
						outp += journals[j].Compress(outp);
				}

				outCompactSize = compactSize;
				outNumDropped = numDroppedJournals;
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void JournalNativeMethodParameters(OperationContext *ctx, Method *method, IObjectManager *objm)
		{
			RDX_TRY(ctx)
			{
				LargeInt numMethodParameters = 0;
				if(method->parameters)
					numMethodParameters = GCInfo::From(method->parameters)->numElements;

				CRef<StackJournal> journals;
				RDX_PROTECT_ASSIGN(ctx, journals, objm->Create1DArray<StackJournal>(ctx, numMethodParameters) );
				LargeInt poffs = 0;
				for(LargeInt i=0;i<numMethodParameters;i++)
				{
					MethodParameter *param = method->parameters + i;

					// This should be caught earlier, but be safe...
					if(param->type == NULL)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);
					bool isPointer = ILPassCompiler::TypeIsRefStruct(objm, param->type);
					// NotNull parameters must be constant
					if(param->isNotNull != FalseValue && param->isConstant == FalseValue)
						RDX_STHROW(ctx, Errors::RDX_ERROR_PACKAGE_CORRUPTED);

					{
						StackJournal sj;
						sj.isParameter = true;
						sj.isFromLocal = false;
						sj.isVarying = (param->type == objm->GetBuiltIns()->st_Varying);
						sj.startInstruction = 0;
						sj.endInstruction = 0;
						sj.isPointer = isPointer;
						sj.pointerSourceType = Programmability::PST_Parameter;
						sj.vType = param->type;
						sj.name = NULL;
						sj.isConstant = (param->isConstant != FalseValue);
						sj.isNotNull = (param->isNotNull != FalseValue);
						sj.notNullInstruction = 0;

						LargeInt psize = 0;
						LargeInt palign = 0;
						if(sj.isPointer)
							psize = sizeof(RDX::Programmability::RuntimePointer<void>);
						else if(sj.isVarying)
							psize = sizeof(RDX::Programmability::TypedRuntimePointer);
						else
							RDX_PROTECT(ctx, objm->TypeValueSize(ctx, param->type, psize, palign) );

						poffs += PaddedSize(psize, sizeof(RuntimeStackValue));
						sj.offset = -poffs;
						sj.pointerSource = sj.offset;

						if(i == method->thisParameterOffset - 1)
							method->_native.thisParameterInvokeOffset = sj.offset;

						journals[i] = sj;
					}
				}

				LargeInt compactSize;
				LargeInt numDroppedJournals;
				CompactJournals(ctx, method, journals, numMethodParameters, objm, compactSize, numDroppedJournals);
				method->_native.numJournals = numMethodParameters - numDroppedJournals;
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void CompileMethod(OperationContext *ctx, Method *m, IObjectManager *objm)
		{
			RDX_TRY(ctx)
			{
				const String *methodName = GCInfo::From(m)->gstSymbol;
				ILPassCompiler ilp;

				if(m->bytecode == NULL)
				{
					if(m->_native.isNativeCall)
						RDX_PROTECT(ctx, JournalNativeMethodParameters(ctx, m, objm));
					return;
				}

				LargeInt numInstructions = m->numInstructions;
				if(numInstructions < 0)
					RDX_STHROW(ctx, Errors::RDX_ERROR_IL_INVALID_OPERAND);

				PushList<VStackValue> opstack;
				PushList<VLocal> localstack;
				PushList<ILInstruction> instructions;
				PushList<UInt8> instructionResumeFlags;
				PushList<ILOpCompactArg> compactArgs;
				PushList<ILOpLargeArg> largeArgs;
				PushList<VStackJournal> stackjournal;
				PushList<StackJournal> journal;
				PushList<ExceptionHandlerJournal> exHandlers;

				CRef<InstructionMeta> metalist;
				RDX_PROTECT_ASSIGN(ctx, metalist, objm->Create1DArray<InstructionMeta>(ctx, numInstructions) );
				CRef<ILDecodedOp> decOps;
				RDX_PROTECT_ASSIGN(ctx, decOps, objm->Create1DArray<ILDecodedOp>(ctx, numInstructions) );

				RDX_PROTECT(ctx, ILPassCompiler::DecodeOps(ctx, m, objm, decOps));
				
				ilp.metalist = metalist;
				ilp.method = m;
				ilp.objm = objm;
				ilp.opstack = &opstack;
				ilp.localstack = &localstack;
				ilp.instructions = &instructions;
				ilp.instructionResumeFlags = &instructionResumeFlags;
				ilp.compactArgs = &compactArgs;
				ilp.largeArgs = &largeArgs;
				ilp.stackjournal = &stackjournal;
				ilp.exHandlers = &exHandlers;
				ilp.journal = &journal;
				ilp.decOps = decOps;
				ilp.localOffsetMaximum = 0;
				ilp.opstackOffsetMaximum = 0;
				ilp.sequentialID = 0;

				{
					CRef<const String> str;
					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.Exception") );
					ilp.st_Exception = objm->LookupSymbolSimple(ctx, str).Cast<StructuredType>();
					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.hashcode") );
					ilp.st_HashCode = objm->LookupSymbolSimple(ctx, str).Cast<StructuredType>();
				}

				ilp.pass = PASS_CreateStacks;
				RDX_PROTECT(ctx, ilp.CompilePass(ctx) );
				RDX_PROTECT_ASSIGN(ctx, opstack.list, objm->Create1DArray<VStackValue>(ctx, opstack.maximum) );
				RDX_PROTECT_ASSIGN(ctx, localstack.list, objm->Create1DArray<VLocal>(ctx, localstack.maximum) );
				RDX_PROTECT_ASSIGN(ctx, stackjournal.list, objm->Create1DArray<VStackJournal>(ctx, stackjournal.maximum) );

				ilp.pass = PASS_GenerateCode;
				instructions.count = 0;
				instructionResumeFlags.count = 0;
				compactArgs.count = 0;
				largeArgs.count = 0;
				journal.count = 0;
				stackjournal.count = 0;
				exHandlers.count = 0;
				RDX_PROTECT(ctx, ilp.CompilePass(ctx) );

				// Round up the size of the localstack to the opstack boundary
				LargeInt lsSize = ilp.localOffsetMaximum;

				if(!CheckAddOverflow(lsSize, (sizeof(RuntimeStackValue) - 1)))
					RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);

				lsSize += (sizeof(RuntimeStackValue) - 1);
				lsSize -= lsSize % sizeof(RuntimeStackValue);
				ilp.localOffsetMaximum = lsSize;

				m->_native.exceptionInsertionOffset = -(lsSize + ilp.OpstackUsed(ctx, VST_Value, NULL) );

				RDX_PROTECT_ASSIGN(ctx, instructions.list, objm->Create1DArray<ILInstruction>(ctx, instructions.maximum) );
				RDX_PROTECT_ASSIGN(ctx, instructionResumeFlags.list, objm->Create1DArray<UInt8>(ctx, instructionResumeFlags.maximum) );
				RDX_PROTECT_ASSIGN(ctx, compactArgs.list, objm->Create1DArray<ILOpCompactArg>(ctx, compactArgs.maximum) );
				RDX_PROTECT_ASSIGN(ctx, largeArgs.list, objm->Create1DArray<ILOpLargeArg>(ctx, largeArgs.maximum) );
				RDX_PROTECT_ASSIGN(ctx, journal.list, objm->Create1DArray<StackJournal>(ctx, journal.maximum) );
				RDX_PROTECT_ASSIGN(ctx, exHandlers.list, objm->Create1DArray<ExceptionHandlerJournal>(ctx, exHandlers.maximum) );
				instructions.count = 0;
				instructionResumeFlags.count = 0;
				compactArgs.count = 0;
				largeArgs.count = 0;
				journal.count = 0;
				stackjournal.count = 0;
				exHandlers.count = 0;
				RDX_PROTECT(ctx, ilp.CompilePass(ctx) );
				
				// Jump availability is now known, run a stitch pass
				instructions.count = 0;
				instructionResumeFlags.count = 0;
				compactArgs.count = 0;
				largeArgs.count = 0;
				journal.count = 0;
				stackjournal.count = 0;
				exHandlers.count = 0;
				ilp.pass = PASS_StitchJumps;
				RDX_PROTECT(ctx, ilp.CompilePass(ctx) );

				m->_native.ilinstructions = instructions.list;
				m->_native.ilResumeFlags = instructionResumeFlags.list;
				m->_native.numILInstructions = instructions.count;
				m->_native.largeArgs = largeArgs.list;
				m->_native.compactArgs = compactArgs.list;
				
				LargeInt extraPad = ilp.opstackOffsetMaximum % RDX_MAX_ALIGNMENT;
				if(extraPad)
					extraPad = RDX_MAX_ALIGNMENT - extraPad;
				if(!CheckAddOverflow(ilp.opstackOffsetMaximum, extraPad))
					RDX_STHROW(ctx, Errors::RDX_ERROR_INTEGER_OVERFLOW);

				m->_native.frameCapacity = ilp.opstackOffsetMaximum + extraPad;

				m->_native.exHandlers = exHandlers.list;

				RDX_PROTECT_ASSIGN(ctx, m->_native.translation1, objm->Create1DArray<LargeInt>(ctx, numInstructions) );
				for(LargeInt i=0;i<numInstructions;i++)
				{
					m->_native.translation1[i] = metalist[i].translatedInstrNum;
				}
				
				// Move everything out of translation space
				for(LargeInt i=0;i<journal.count;i++)
				{
					const InstructionMeta *endMeta = metalist + journal.list[i].endInstruction;
					const InstructionMeta *startMeta = metalist + journal.list[i].startInstruction;

					journal.list[i].startInstruction = startMeta->translatedInstrNum + startMeta->numValidationInstrs;
					journal.list[i].endInstruction = endMeta->translatedInstrNum + endMeta->numValidationInstrs;
				}
				
				if(journal.maximum)
				{
					LargeInt compactSize;
					LargeInt numDroppedJournals;
					CompactJournals(ctx, m, journal.list, journal.maximum, objm, compactSize, numDroppedJournals);
					m->_native.numJournals = journal.maximum - numDroppedJournals;
				}

				for(LargeInt i=0;i<exHandlers.count;i++)
				{
					const ExceptionHandlerJournal *exj = exHandlers.list + i;
					const InstructionMeta *endMeta = metalist + exj->endInstruction;
					exHandlers.list[i].startInstruction = metalist[exj->startInstruction].translatedInstrNum;
					exHandlers.list[i].endInstruction = endMeta->translatedInstrNum + endMeta->numValidationInstrs;
					exHandlers.list[i].handlerInstruction = ilp.FindJumpTarget(metalist[exj->handlerInstruction].translatedInstrNum);
				}

				// Create call points and debug info, flag resumption points
				{
					LargeInt numCallPoints = 0;
					LargeInt numDebugInfo = 0;

					for(LargeInt i=0;i<instructions.count;i++)
					{
						bool shouldMarkNext = false;
						switch(instructions.list[i].opcode)
						{
						case ILOP_tick:
							shouldMarkNext = true;
							break;
						case ILOP_call:
						case ILOP_callvirtual:
						case ILOP_callinterface:
						case ILOP_calldelegatebp:
						case ILOP_calldelegateprv:
							shouldMarkNext = true;
							numCallPoints++;
							break;
						case ILOP_debuginfo:
							numDebugInfo++;
							break;
						default:
							break;
						}

						if(shouldMarkNext)
							instructionResumeFlags.list[(i + 1) / 8] |= (1 << ((i + 1) % 8));
					}
					
					if(numCallPoints)
					{
						RDX_PROTECT_ASSIGN(ctx, m->_native.callPoints, objm->Create1DArray<ILCallPoint>(ctx, numCallPoints));
					}
					if(numDebugInfo)
					{
						RDX_PROTECT_ASSIGN(ctx, m->_native.debugInfo, objm->Create1DArray<ILDebugInfo>(ctx, numDebugInfo));
					}
					
					ILDebugInfo *outDebugInfo = m->_native.debugInfo;
					ILCallPoint *outCallPoint = m->_native.callPoints;

					for(LargeInt i=0;i<instructions.count;i++)
					{
						const ILInstruction *instr = instructions.list + i;
						switch(instr->opcode)
						{
						case ILOP_call:
						case ILOP_calldelegatebp:
						case ILOP_calldelegateprv:
						case ILOP_callvirtual:
						case ILOP_callinterface:
							{
								outCallPoint->ilOpcode = instr->opcode;
								outCallPoint->instrNum = i;
								outCallPoint->paramBaseOffset = instr->arg.ca[0].li;
								outCallPoint->prvOffset = instr->arg.ca[1].li;

								switch(instr->opcode)
								{
								case ILOP_call:
									outCallPoint->args.call.func = static_cast<const Method *>(instr->arg.ca[2].p);
									break;
								case ILOP_calldelegatebp:
								case ILOP_calldelegateprv:
									outCallPoint->args.callDelegate.offset = instr->arg.ca[2].li;
									outCallPoint->args.callDelegate.bpRelative = (instr->opcode == ILOP_calldelegatebp);
									break;
								case ILOP_callvirtual:
									outCallPoint->args.callVirtual.bpOffset = instr->arg.ca[2].li;
									outCallPoint->args.callVirtual.vftOffset = instr->arg.ca[3].li;
									break;
								case ILOP_callinterface:
									outCallPoint->args.callInterface.bpOffset = instr->arg.ca[2].li;
									outCallPoint->args.callInterface.vftOffset = instr->arg.ca[3].li;
									outCallPoint->args.callInterface.soughtInterface = static_cast<const StructuredType *>(instr->arg.ca[4].p);
									break;
								};

								outCallPoint++;
							}
							break;
						case ILOP_debuginfo:
							{
								outDebugInfo->firstInstruction = i;
								outDebugInfo->filename = static_cast<const String *>(instr->arg.ca[0].p);
								outDebugInfo->line = instr->arg.ca[1].li;
								outDebugInfo++;
							}
							break;
						default:
							break;
						}
					}
				}
				
				RDX_VERBOSE( RDX::Intrinsics::DisassembleMethod(m) );

#ifdef RDX_DEBUG_NONVERBOSE_DISASSEMBLE
				FILE *f;
				fopen_s(&f, "disassembly.rdxil", "a");
				RDX::Intrinsics::DisassembleMethod(f, m);
				fclose(f);
#endif

				RDX_PROTECT(ctx, objm->GetCodeProvider()->CreateExecutable(objm, ctx, m) );
				m->_native.nativeCall = NULL;//objm->GetCodeProvider()->GetNativeCallback();
				m->_native.isNativeCall = false;
				m->_native.ipToCurrentInstruction = objm->GetCodeProvider()->GetIPToCurrentInstructionCallback();
				m->_native.instrNumToIP = objm->GetCodeProvider()->GetInstrNumToIPCallback();
				m->_native.resumeThread = objm->GetCodeProvider()->GetResumeThreadCallback(objm);

				//ilp.pass = PASS_GenerateCode;
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}
	};
};
